Merge "Revert^2 "[framework] Integrate new quantizers"" into sc-dev
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp
index 19a66ad..638403d 100644
--- a/apct-tests/perftests/contentcapture/Android.bp
+++ b/apct-tests/perftests/contentcapture/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ContentCapturePerfTests",
srcs: ["src/**/*.java"],
diff --git a/apct-tests/perftests/inputmethod/Android.bp b/apct-tests/perftests/inputmethod/Android.bp
index 463ac9b..f2f1f75 100644
--- a/apct-tests/perftests/inputmethod/Android.bp
+++ b/apct-tests/perftests/inputmethod/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ImePerfTests",
srcs: ["src/**/*.java"],
diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp
index b014fdc..87f65a9 100644
--- a/apex/appsearch/Android.bp
+++ b/apex/appsearch/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
apex {
name: "com.android.appsearch",
manifest: "apex_manifest.json",
@@ -21,6 +30,7 @@
],
key: "com.android.appsearch.key",
certificate: ":com.android.appsearch.certificate",
+ updatable: false,
}
apex_key {
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 8ba7954..5def55f 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "framework-appsearch-sources",
srcs: [
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 905000a..cc79f6b 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -2,6 +2,7 @@
package android.app.appsearch {
public final class AppSearchBatchResult<KeyType, ValueType> {
+ method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getAll();
method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getFailures();
method @NonNull public java.util.Map<KeyType,ValueType> getSuccesses();
method public boolean isSuccess();
@@ -146,7 +147,7 @@
method public void put(@NonNull android.app.appsearch.PutDocumentsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
method public void remove(@NonNull android.app.appsearch.RemoveByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
method public void remove(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
- method @NonNull public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
+ method public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
}
@@ -358,7 +359,6 @@
method @NonNull public java.util.Set<java.lang.String> getIncompatibleTypes();
method @NonNull public java.util.Set<java.lang.String> getMigratedTypes();
method @NonNull public java.util.List<android.app.appsearch.SetSchemaResponse.MigrationFailure> getMigrationFailures();
- method public boolean isSuccess();
}
public static class SetSchemaResponse.MigrationFailure {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
index cd75b14..519c14f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
@@ -100,7 +100,6 @@
* Returns a {@link Map} of all keys mapped to the {@link AppSearchResult}s they produced.
*
* <p>The values of the {@link Map} will not be {@code null}.
- * @hide
*/
@NonNull
public Map<KeyType, AppSearchResult<ValueType>> getAll() {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 69d4e53..6a5975e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -36,12 +36,89 @@
import java.util.function.Consumer;
/**
- * This class provides access to the centralized AppSearch index maintained by the system.
+ * Provides access to the centralized AppSearch index maintained by the system.
*
- * <p>Apps can index structured text documents with AppSearch, which can then be retrieved through
- * the query API.
+ * <p>AppSearch is a search library for managing structured data featuring:
+ * <ul>
+ * <li>A fully offline on-device solution
+ * <li>A set of APIs for applications to index documents and retrieve them via full-text search
+ * <li>APIs for applications to allow the System to display their content on system UI surfaces
+ * <li>Similarly, APIs for applications to allow the System to share their content with other
+ * specified applications.
+ * </ul>
+ *
+ * <p>Applications create a database by opening an {@link AppSearchSession}.
+ *
+ * <p>Example:
+ * <pre>
+ * AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
+ *
+ * AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder().
+ * setDatabaseName(dbName).build());
+ * appSearchManager.createSearchSession(searchContext, mExecutor, appSearchSessionResult -> {
+ * mAppSearchSession = appSearchSessionResult.getResultValue();
+ * });</pre>
+ *
+ * <p>After opening the session, a schema must be set in order to define the organizational
+ * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema
+ * is composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique
+ * type of data.
+ *
+ * <p>Example:
+ * <pre>
+ * AppSearchSchema emailSchemaType = new AppSearchSchema.Builder("Email")
+ * .addProperty(new StringPropertyConfig.Builder("subject")
+ * .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ * .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ * .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ * .build()
+ * ).build();
+ *
+ * SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(emailSchemaType).build();
+ * mAppSearchSession.set(request, mExecutor, appSearchResult -> {
+ * if (appSearchResult.isSuccess()) {
+ * //Schema has been successfully set.
+ * }
+ * });</pre>
+ *
+ * <p>The basic unit of data in AppSearch is represented as a {@link GenericDocument} object,
+ * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a
+ * logical group of documents. For example, a namespace can be created to group documents on a
+ * per-account basis. A URI identifies a single document within a namespace. The combination
+ * of URI and namespace uniquely identifies a {@link GenericDocument} in the database.
+ *
+ * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database
+ * and indexed by calling {@link AppSearchSession#put}.
+ *
+ * <p>Example:
+ * <pre>
+ * // Although for this example we use GenericDocument directly, we recommend extending
+ * // GenericDocument to create specific types (i.e. Email) with specific setters/getters.
+ * GenericDocument email = new GenericDocument.Builder<>(URI, EMAIL_SCHEMA_TYPE)
+ * .setNamespace(NAMESPACE)
+ * .setPropertyString(“subject”, EMAIL_SUBJECT)
+ * .setScore(EMAIL_SCORE)
+ * .build();
+ *
+ * PutDocumentsRequest request = new PutDocumentsRequest.Builder().addGenericDocuments(email)
+ * .build();
+ * mAppSearchSession.put(request, mExecutor, appSearchBatchResult -> {
+ * if (appSearchBatchResult.isSuccess()) {
+ * //All documents have been successfully indexed.
+ * }
+ * });</pre>
+ *
+ * <p>Searching within the database is done by calling {@link AppSearchSession#search} and providing
+ * the query string to search for, as well as a {@link SearchSpec}.
+ *
+ * <p>Alternatively, {@link AppSearchSession#getByUri} can be called to retrieve documents by URI
+ * and namespace.
+ *
+ * <p>Document removal is done either by time-to-live expiration, or explicitly calling a remove
+ * operation. Remove operations can be done by URI and namespace via
+ * {@link AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)},
+ * or by query via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}.
*/
-// TODO(b/148046169): This class header needs a detailed example/tutorial.
@SystemService(Context.APP_SEARCH_SERVICE)
public class AppSearchManager {
/**
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 73ca0cc..3c02d10 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -497,7 +497,6 @@
* @param callback Callback to receive errors. If the operation succeeds, the callback will be
* invoked with {@code null}.
*/
- @NonNull
public void reportUsage(
@NonNull ReportUsageRequest request,
@NonNull @CallbackExecutor Executor executor,
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 138eb23..72bb9f3 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -583,9 +583,9 @@
* @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}.
+ * 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 uri, @NonNull String schemaType) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
index 05b2128..57700f8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
@@ -28,9 +28,11 @@
import java.util.List;
/**
- * Encapsulates a request to index a document into an {@link AppSearchSession} database.
+ * Encapsulates a request to index documents into an {@link AppSearchSession} database.
*
* <p>@see AppSearchSession#putDocuments
+ *
+ * @see AppSearchSession#put
*/
public final class PutDocumentsRequest {
private final List<GenericDocument> mDocuments;
@@ -39,7 +41,7 @@
mDocuments = documents;
}
- /** Returns the documents that are part of this request. */
+ /** Returns a list of {@link GenericDocument} objects that are part of this request. */
@NonNull
public List<GenericDocument> getGenericDocuments() {
return Collections.unmodifiableList(mDocuments);
@@ -54,14 +56,22 @@
private final List<GenericDocument> mDocuments = new ArrayList<>();
private boolean mBuilt = false;
- /** Adds one or more {@link GenericDocument} objects to the request. */
+ /**
+ * Adds one or more {@link GenericDocument} objects to the request.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
public Builder addGenericDocuments(@NonNull GenericDocument... documents) {
Preconditions.checkNotNull(documents);
return addGenericDocuments(Arrays.asList(documents));
}
- /** Adds a collection of {@link GenericDocument} objects to the request. */
+ /**
+ * Adds a collection of {@link GenericDocument} objects to the request.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
public Builder addGenericDocuments(
@NonNull Collection<? extends GenericDocument> documents) {
@@ -71,7 +81,11 @@
return this;
}
- /** Creates a new {@link PutDocumentsRequest} object. */
+ /**
+ * Creates a new {@link PutDocumentsRequest} object.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
public PutDocumentsRequest build() {
Preconditions.checkState(!mBuilt, "Builder has already been used");
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 bc99d4f..a146006 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -79,12 +79,6 @@
return mBundle;
}
- /** TODO(b/177266929): Remove this deprecated method */
- //@Deprecated
- public boolean isSuccess() {
- return true;
- }
-
/**
* Returns a {@link List} of all failed {@link MigrationFailure}.
*
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 2fd5c73..57ee1ec 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -11,6 +11,15 @@
// WITHOUT 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "service-appsearch",
installable: true,
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
index babcd25..7c92456 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -218,9 +218,23 @@
* @throws AppSearchException AppSearchException on AppSearchImpl error.
*/
public void initialize() throws AppSearchException {
- if (!mAppSearchImpl.hasSchemaTypeLocked(PACKAGE_NAME, DATABASE_NAME, VISIBILITY_TYPE)
- || !mAppSearchImpl.hasSchemaTypeLocked(
- PACKAGE_NAME, DATABASE_NAME, PACKAGE_ACCESSIBLE_TYPE)) {
+ List<AppSearchSchema> schemas = mAppSearchImpl.getSchema(PACKAGE_NAME, DATABASE_NAME);
+ boolean hasVisibilityType = false;
+ boolean hasPackageAccessibleType = false;
+ for (int i = 0; i < schemas.size(); i++) {
+ AppSearchSchema schema = schemas.get(i);
+ if (schema.getSchemaType().equals(VISIBILITY_TYPE)) {
+ hasVisibilityType = true;
+ } else if (schema.getSchemaType().equals(PACKAGE_ACCESSIBLE_TYPE)) {
+ hasPackageAccessibleType = true;
+ }
+
+ if (hasVisibilityType && hasPackageAccessibleType) {
+ // Found both our types, can exit early.
+ break;
+ }
+ }
+ if (!hasVisibilityType || !hasPackageAccessibleType) {
// Schema type doesn't exist yet. Add it.
mAppSearchImpl.setSchema(
PACKAGE_NAME,
@@ -250,10 +264,11 @@
/*typePropertyPaths=*/ Collections.emptyMap());
// Update platform visibility settings
- String[] schemas =
+ String[] notPlatformSurfaceableSchemas =
document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY);
- if (schemas != null) {
- mNotPlatformSurfaceableMap.put(prefix, new ArraySet<>(Arrays.asList(schemas)));
+ if (notPlatformSurfaceableSchemas != null) {
+ mNotPlatformSurfaceableMap.put(
+ prefix, new ArraySet<>(Arrays.asList(notPlatformSurfaceableSchemas)));
}
// Update 3p package visibility settings
@@ -333,7 +348,7 @@
schemasPackageAccessible.entrySet()) {
for (int i = 0; i < entry.getValue().size(); i++) {
GenericDocument packageAccessibleDocument =
- new GenericDocument.Builder(/*uri=*/"", PACKAGE_ACCESSIBLE_TYPE)
+ new GenericDocument.Builder(/*uri=*/ "", PACKAGE_ACCESSIBLE_TYPE)
.setNamespace(NAMESPACE)
.setPropertyString(
PACKAGE_NAME_PROPERTY,
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
new file mode 100644
index 0000000..0f23d92
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.external.localstorage;
+
+import android.annotation.NonNull;
+import android.app.appsearch.exceptions.AppSearchException;
+
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+
+/**
+ * An interface for implementing client-defined logging AppSearch operations stats.
+ *
+ * <p>Any implementation needs to provide general information on how to log all the stats types.
+ * (e.g. {@link CallStats})
+ *
+ * <p>All implementations of this interface must be thread safe.
+ *
+ * @hide
+ */
+public interface AppSearchLogger {
+ /** Logs {@link CallStats} */
+ void logStats(@NonNull CallStats stats) throws AppSearchException;
+
+ /** Logs {@link PutDocumentStats} */
+ void logStats(@NonNull PutDocumentStats stats) throws AppSearchException;
+
+ // TODO(b/173532925) Add remaining logStats once we add all the stats.
+}
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 a501e99..a7f1cc4 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
@@ -76,7 +76,7 @@
int currentVersion = mCurrentVersionMap.get(schemaType);
int finalVersion = mFinalVersionMap.get(schemaType);
try (FileOutputStream outputStream = new FileOutputStream(mFile)) {
- // TODO(b/177266929) change the output stream so that we can use it in platform
+ // TODO(b/151178558) change the output stream so that we can use it in platform
CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream);
SearchResultPage searchResultPage =
mAppSearchImpl.query(
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
new file mode 100644
index 0000000..81a5067
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+package com.android.server.appsearch.external.localstorage.stats;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A class for setting basic information to log for all function calls.
+ *
+ * <p>This class can set which stats to log for both batch and non-batch {@link
+ * android.app.appsearch.AppSearchSession} calls.
+ *
+ * <p>Some function calls like {@link android.app.appsearch.AppSearchSession#setSchema} have their
+ * own detailed stats class {@link placeholder}. However, {@link CallStats} can still be used along
+ * with the detailed stats class for easy aggregation/analysis with other function calls.
+ *
+ * @hide
+ */
+public class CallStats {
+ @IntDef(
+ value = {
+ CALL_TYPE_UNKNOWN,
+ CALL_TYPE_INITIALIZE,
+ CALL_TYPE_SET_SCHEMA,
+ CALL_TYPE_PUT_DOCUMENTS,
+ CALL_TYPE_GET_DOCUMENTS,
+ CALL_TYPE_REMOVE_DOCUMENTS,
+ CALL_TYPE_PUT_DOCUMENT,
+ CALL_TYPE_GET_DOCUMENT,
+ CALL_TYPE_REMOVE_DOCUMENT,
+ CALL_TYPE_QUERY,
+ CALL_TYPE_OPTIMIZE,
+ CALL_TYPE_FLUSH,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallType {}
+
+ public static final int CALL_TYPE_UNKNOWN = 0;
+ public static final int CALL_TYPE_INITIALIZE = 1;
+ public static final int CALL_TYPE_SET_SCHEMA = 2;
+ public static final int CALL_TYPE_PUT_DOCUMENTS = 3;
+ public static final int CALL_TYPE_GET_DOCUMENTS = 4;
+ public static final int CALL_TYPE_REMOVE_DOCUMENTS = 5;
+ public static final int CALL_TYPE_PUT_DOCUMENT = 6;
+ public static final int CALL_TYPE_GET_DOCUMENT = 7;
+ public static final int CALL_TYPE_REMOVE_DOCUMENT = 8;
+ public static final int CALL_TYPE_QUERY = 9;
+ public static final int CALL_TYPE_OPTIMIZE = 10;
+ public static final int CALL_TYPE_FLUSH = 11;
+
+ @NonNull private final GeneralStats mGeneralStats;
+ @CallType private final int mCallType;
+ private final int mEstimatedBinderLatencyMillis;
+ private final int mNumOperationsSucceeded;
+ private final int mNumOperationsFailed;
+
+ CallStats(@NonNull Builder builder) {
+ Preconditions.checkNotNull(builder);
+ mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats);
+ mCallType = builder.mCallType;
+ mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis;
+ mNumOperationsSucceeded = builder.mNumOperationsSucceeded;
+ mNumOperationsFailed = builder.mNumOperationsFailed;
+ }
+
+ /** Returns general information for the call. */
+ @NonNull
+ public GeneralStats getGeneralStats() {
+ return mGeneralStats;
+ }
+
+ /** Returns type of the call. */
+ @CallType
+ public int getCallType() {
+ return mCallType;
+ }
+
+ /** Returns estimated binder latency, in milliseconds */
+ public int getEstimatedBinderLatencyMillis() {
+ return mEstimatedBinderLatencyMillis;
+ }
+
+ /**
+ * Returns number of operations succeeded.
+ *
+ * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total
+ * number of individual successful put operations. In this case, how many documents are
+ * successfully indexed.
+ *
+ * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, the
+ * sum of {@link CallStats#getNumOperationsSucceeded()} and {@link
+ * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation.
+ */
+ public int getNumOperationsSucceeded() {
+ return mNumOperationsSucceeded;
+ }
+
+ /**
+ * Returns number of operations failed.
+ *
+ * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total
+ * number of individual failed put operations. In this case, how many documents are failed to be
+ * indexed.
+ *
+ * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, the
+ * sum of {@link CallStats#getNumOperationsSucceeded()} and {@link
+ * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation.
+ */
+ public int getNumOperationsFailed() {
+ return mNumOperationsFailed;
+ }
+
+ /** Builder for {@link CallStats}. */
+ public static class Builder {
+ @NonNull final GeneralStats mGeneralStats;
+ @CallType int mCallType;
+ int mEstimatedBinderLatencyMillis;
+ int mNumOperationsSucceeded;
+ int mNumOperationsFailed;
+
+ /** Builder takes {@link GeneralStats} to hold general stats. */
+ public Builder(@NonNull GeneralStats generalStats) {
+ mGeneralStats = Preconditions.checkNotNull(generalStats);
+ }
+
+ /** Sets type of the call. */
+ @NonNull
+ public Builder setCallType(@CallType int callType) {
+ mCallType = callType;
+ return this;
+ }
+
+ /** Sets estimated binder latency, in milliseconds. */
+ @NonNull
+ public Builder setEstimatedBinderLatencyMillis(int estimatedBinderLatencyMillis) {
+ mEstimatedBinderLatencyMillis = estimatedBinderLatencyMillis;
+ return this;
+ }
+
+ /**
+ * Sets number of operations succeeded.
+ *
+ * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total
+ * number of individual successful put operations. In this case, how many documents are
+ * successfully indexed.
+ *
+ * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema},
+ * the sum of {@link CallStats#getNumOperationsSucceeded()} and {@link
+ * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation.
+ */
+ @NonNull
+ public Builder setNumOperationsSucceeded(int numOperationsSucceeded) {
+ mNumOperationsSucceeded = numOperationsSucceeded;
+ return this;
+ }
+
+ /**
+ * Sets number of operations failed.
+ *
+ * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total
+ * number of individual failed put operations. In this case, how many documents are failed
+ * to be indexed.
+ *
+ * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema},
+ * the sum of {@link CallStats#getNumOperationsSucceeded()} and {@link
+ * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation.
+ */
+ @NonNull
+ public Builder setNumOperationsFailed(int numOperationsFailed) {
+ mNumOperationsFailed = numOperationsFailed;
+ return this;
+ }
+
+ /** Creates {@link CallStats} object from {@link Builder} instance. */
+ @NonNull
+ public CallStats build() {
+ return new CallStats(/* builder= */ this);
+ }
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java
new file mode 100644
index 0000000..d2a45d5
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+package com.android.server.appsearch.external.localstorage.stats;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class for holding general logging information.
+ *
+ * <p>This class cannot be logged by {@link
+ * com.android.server.appsearch.external.localstorage.AppSearchLogger} directly. It is used for
+ * defining general logging information that is shared across different stats classes.
+ *
+ * @see PutDocumentStats
+ * @see CallStats
+ * @hide
+ */
+public final class GeneralStats {
+ /** Package name of the application. */
+ @NonNull private final String mPackageName;
+
+ /** Database name within AppSearch. */
+ @NonNull private final String mDatabase;
+
+ /**
+ * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal
+ * state.
+ */
+ @AppSearchResult.ResultCode private final int mStatusCode;
+
+ private final int mTotalLatencyMillis;
+
+ GeneralStats(@NonNull Builder builder) {
+ Preconditions.checkNotNull(builder);
+ mPackageName = Preconditions.checkNotNull(builder.mPackageName);
+ mDatabase = Preconditions.checkNotNull(builder.mDatabase);
+ mStatusCode = builder.mStatusCode;
+ mTotalLatencyMillis = builder.mTotalLatencyMillis;
+ }
+
+ /** Returns package name. */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /** Returns database name. */
+ @NonNull
+ public String getDatabase() {
+ return mDatabase;
+ }
+
+ /** Returns result code from {@link AppSearchResult#getResultCode()} */
+ @AppSearchResult.ResultCode
+ public int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /** Returns total latency, in milliseconds. */
+ public int getTotalLatencyMillis() {
+ return mTotalLatencyMillis;
+ }
+
+ /** Builder for {@link GeneralStats}. */
+ public static class Builder {
+ @NonNull final String mPackageName;
+ @NonNull final String mDatabase;
+ @AppSearchResult.ResultCode int mStatusCode;
+ int mTotalLatencyMillis;
+
+ /**
+ * Constructor
+ *
+ * @param packageName name of the package logging stats
+ * @param dataBase name of the database logging stats
+ */
+ public Builder(@NonNull String packageName, @NonNull String dataBase) {
+ mPackageName = Preconditions.checkNotNull(packageName);
+ mDatabase = Preconditions.checkNotNull(dataBase);
+ }
+
+ /** Sets status code returned from {@link AppSearchResult#getResultCode()} */
+ @NonNull
+ public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
+ mStatusCode = statusCode;
+ return this;
+ }
+
+ /** Sets total latency, in milliseconds. */
+ @NonNull
+ public Builder setTotalLatencyMillis(int totalLatencyMillis) {
+ mTotalLatencyMillis = totalLatencyMillis;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link GeneralStats} object from the contents of this {@link Builder}
+ * instance.
+ */
+ @NonNull
+ public GeneralStats build() {
+ return new GeneralStats(/* builder= */ this);
+ }
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java
new file mode 100644
index 0000000..b1b643b
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+package com.android.server.appsearch.external.localstorage.stats;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class for holding detailed stats to log for each individual document put by a {@link
+ * android.app.appsearch.AppSearchSession#put} call.
+ *
+ * @hide
+ */
+public final class PutDocumentStats {
+ /** {@link GeneralStats} holds the general stats. */
+ @NonNull private final GeneralStats mGeneralStats;
+
+ /** Time used to generate a document proto from a Bundle. */
+ private final int mGenerateDocumentProtoLatencyMillis;
+
+ /** Time used to rewrite types and namespaces in the document. */
+ private final int mRewriteDocumentTypesLatencyMillis;
+
+ /** Overall time used for the native function call. */
+ private final int mNativeLatencyMillis;
+
+ /** Time used to store the document. */
+ private final int mNativeDocumentStoreLatencyMillis;
+
+ /** Time used to index the document. It doesn't include the time to merge indices. */
+ private final int mNativeIndexLatencyMillis;
+
+ /** Time used to merge the indices. */
+ private final int mNativeIndexMergeLatencyMillis;
+
+ /** Document size in bytes. */
+ private final int mNativeDocumentSizeBytes;
+
+ /** Number of tokens added to the index. */
+ private final int mNativeNumTokensIndexed;
+
+ /** Number of tokens clipped for exceeding the max number. */
+ private final int mNativeNumTokensClipped;
+
+ PutDocumentStats(@NonNull Builder builder) {
+ Preconditions.checkNotNull(builder);
+ mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats);
+ mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis;
+ mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis;
+ mNativeLatencyMillis = builder.mNativeLatencyMillis;
+ mNativeDocumentStoreLatencyMillis = builder.mNativeDocumentStoreLatencyMillis;
+ mNativeIndexLatencyMillis = builder.mNativeIndexLatencyMillis;
+ mNativeIndexMergeLatencyMillis = builder.mNativeIndexMergeLatencyMillis;
+ mNativeDocumentSizeBytes = builder.mNativeDocumentSizeBytes;
+ mNativeNumTokensIndexed = builder.mNativeNumTokensIndexed;
+ mNativeNumTokensClipped = builder.mNativeNumTokensClipped;
+ }
+
+ /** Returns the {@link GeneralStats} object attached to this instance. */
+ @NonNull
+ public GeneralStats getGeneralStats() {
+ return mGeneralStats;
+ }
+
+ /** Returns time spent on generating document proto, in milliseconds. */
+ public int getGenerateDocumentProtoLatencyMillis() {
+ return mGenerateDocumentProtoLatencyMillis;
+ }
+
+ /** Returns time spent on rewriting types and namespaces in document, in milliseconds. */
+ public int getRewriteDocumentTypesLatencyMillis() {
+ return mRewriteDocumentTypesLatencyMillis;
+ }
+
+ /** Returns time spent in native, in milliseconds. */
+ public int getNativeLatencyMillis() {
+ return mNativeLatencyMillis;
+ }
+
+ /** Returns time spent on document store, in milliseconds. */
+ public int getNativeDocumentStoreLatencyMillis() {
+ return mNativeDocumentStoreLatencyMillis;
+ }
+
+ /** Returns time spent on indexing, in milliseconds. */
+ public int getNativeIndexLatencyMillis() {
+ return mNativeIndexLatencyMillis;
+ }
+
+ /** Returns time spent on merging indices, in milliseconds. */
+ public int getNativeIndexMergeLatencyMillis() {
+ return mNativeIndexMergeLatencyMillis;
+ }
+
+ /** Returns document size, in bytes. */
+ public int getNativeDocumentSizeBytes() {
+ return mNativeDocumentSizeBytes;
+ }
+
+ /** Returns number of tokens indexed. */
+ public int getNativeNumTokensIndexed() {
+ return mNativeNumTokensIndexed;
+ }
+
+ /** Returns number of tokens clipped for exceeding the max number. */
+ public int getNativeNumTokensClipped() {
+ return mNativeNumTokensClipped;
+ }
+
+ /** Builder for {@link PutDocumentStats}. */
+ public static class Builder {
+ @NonNull final GeneralStats mGeneralStats;
+ int mGenerateDocumentProtoLatencyMillis;
+ int mRewriteDocumentTypesLatencyMillis;
+ int mNativeLatencyMillis;
+ int mNativeDocumentStoreLatencyMillis;
+ int mNativeIndexLatencyMillis;
+ int mNativeIndexMergeLatencyMillis;
+ int mNativeDocumentSizeBytes;
+ int mNativeNumTokensIndexed;
+ int mNativeNumTokensClipped;
+
+ /** Builder takes {@link GeneralStats} to hold general stats. */
+ public Builder(@NonNull GeneralStats generalStats) {
+ mGeneralStats = Preconditions.checkNotNull(generalStats);
+ }
+
+ /** Sets how much time we spend for generating document proto, in milliseconds. */
+ @NonNull
+ public Builder setGenerateDocumentProtoLatencyMillis(
+ int generateDocumentProtoLatencyMillis) {
+ mGenerateDocumentProtoLatencyMillis = generateDocumentProtoLatencyMillis;
+ return this;
+ }
+
+ /**
+ * Sets how much time we spend for rewriting types and namespaces in document, in
+ * milliseconds.
+ */
+ @NonNull
+ public Builder setRewriteDocumentTypesLatencyMillis(int rewriteDocumentTypesLatencyMillis) {
+ mRewriteDocumentTypesLatencyMillis = rewriteDocumentTypesLatencyMillis;
+ return this;
+ }
+
+ /** Sets the native latency, in milliseconds. */
+ @NonNull
+ public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
+ mNativeLatencyMillis = nativeLatencyMillis;
+ return this;
+ }
+
+ /** Sets how much time we spend on document store, in milliseconds. */
+ @NonNull
+ public Builder setNativeDocumentStoreLatencyMillis(int nativeDocumentStoreLatencyMillis) {
+ mNativeDocumentStoreLatencyMillis = nativeDocumentStoreLatencyMillis;
+ return this;
+ }
+
+ /** Sets the native index latency, in milliseconds. */
+ @NonNull
+ public Builder setNativeIndexLatencyMillis(int nativeIndexLatencyMillis) {
+ mNativeIndexLatencyMillis = nativeIndexLatencyMillis;
+ return this;
+ }
+
+ /** Sets how much time we spend on merging indices, in milliseconds. */
+ @NonNull
+ public Builder setNativeIndexMergeLatencyMillis(int nativeIndexMergeLatencyMillis) {
+ mNativeIndexMergeLatencyMillis = nativeIndexMergeLatencyMillis;
+ return this;
+ }
+
+ /** Sets document size, in bytes. */
+ @NonNull
+ public Builder setNativeDocumentSizeBytes(int nativeDocumentSizeBytes) {
+ mNativeDocumentSizeBytes = nativeDocumentSizeBytes;
+ return this;
+ }
+
+ /** Sets number of tokens indexed in native. */
+ @NonNull
+ public Builder setNativeNumTokensIndexed(int nativeNumTokensIndexed) {
+ mNativeNumTokensIndexed = nativeNumTokensIndexed;
+ return this;
+ }
+
+ /** Sets number of tokens clipped for exceeding the max number. */
+ @NonNull
+ public Builder setNativeNumTokensClipped(int nativeNumTokensClipped) {
+ mNativeNumTokensClipped = nativeNumTokensClipped;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link PutDocumentStats} object from the contents of this {@link Builder}
+ * instance.
+ */
+ @NonNull
+ public PutDocumentStats build() {
+ return new PutDocumentStats(/* builder= */ this);
+ }
+ }
+}
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index d076db3..fc0299e 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ia9a8daef1a6d7d9432f7808d440abd64f4797701
+I895f5fb3bcb4be0642c6193000e57d80aafe2166
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
index 54d5039..eb072af 100644
--- a/apex/appsearch/testing/Android.bp
+++ b/apex/appsearch/testing/Android.bp
@@ -11,6 +11,15 @@
// WITHOUT 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "AppSearchTestUtils",
srcs: ["java/**/*.java"],
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index ea21e19..34c7ccb 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -90,11 +90,11 @@
* <p>It is a no-op to set the same schema as has been previously set; this is handled
* efficiently.
*
- * <p>By default, documents are visible on platform surfaces. To opt out, call
- * {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} with {@code visible} as
- * false. Any visibility settings apply only to the schemas that are included in the
- * {@code request}. Visibility settings for a schema type do not persist across
- * {@link #setSchema} calls.
+ * <p>By default, documents are visible on platform surfaces. To opt out, call {@code
+ * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any
+ * visibility settings apply only to the schemas that are included in the {@code request}.
+ * Visibility settings for a schema type do not apply or persist across {@link
+ * SetSchemaRequest}s.
*
* <p>Migration: make non-backwards-compatible changes will delete all stored documents in old
* schema. You can save your documents by setting {@link
@@ -118,6 +118,8 @@
* @see android.app.appsearch.AppSearchSchema.Migrator
* @see android.app.appsearch.AppSearchMigrationHelper.Transformer
*/
+ // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
+ // exposed.
@NonNull
ListenableFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request);
@@ -132,15 +134,17 @@
ListenableFuture<Set<AppSearchSchema>> getSchema();
/**
- * Indexes documents into AppSearch.
+ * Indexes documents into the {@link AppSearchSessionShim} database.
*
- * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a
- * schema type previously registered via the {@link #setSchema} method.
+ * <p>Each {@link GenericDocument} object must have a {@code schemaType} field set to an {@link
+ * AppSearchSchema} type that has been previously registered by calling the {@link #setSchema}
+ * method.
*
- * @param request {@link PutDocumentsRequest} containing documents to be indexed
- * @return The pending result of performing this operation. The keys of the returned {@link
- * AppSearchBatchResult} are the URIs of the input documents. The values are {@code null} if
- * they were successfully indexed, or a failed {@link AppSearchResult} otherwise.
+ * @param request containing documents to be indexed.
+ * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The
+ * keys of the returned {@link AppSearchBatchResult} are the URIs of the input documents.
+ * The values are either {@code null} if the corresponding document was successfully
+ * indexed, or a failed {@link AppSearchResult} otherwise.
*/
@NonNull
ListenableFuture<AppSearchBatchResult<String, Void>> put(@NonNull PutDocumentsRequest request);
@@ -213,7 +217,7 @@
* adding projection, can be set by calling the corresponding {@link SearchSpec.Builder} setter.
*
* <p>This method is lightweight. The heavy work will be done in {@link
- * SearchResultsShim#getNextPage()}.
+ * SearchResultsShim#getNextPage}.
*
* @param queryExpression query string to search.
* @param searchSpec spec for setting document filters, adding projection, setting term match
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
index 31c934f..d912c08 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
@@ -37,11 +37,11 @@
* SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi}, or {@link
* SetSchemaRequest.Builder#setDocumentClassVisibilityForSystemUi} when building a schema.
*
- * <p>See {@link AppSearchSessionShim#search(String, SearchSpec)} for a detailed explanation on
- * forming a query string.
+ * <p>See {@link AppSearchSessionShim#search} for a detailed explanation on forming a query
+ * string.
*
* <p>This method is lightweight. The heavy work will be done in {@link
- * SearchResultsShim#getNextPage()}.
+ * SearchResultsShim#getNextPage}.
*
* @param queryExpression query string to search.
* @param searchSpec spec for setting document filters, adding projection, setting term match
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java
index 328c65c..38f61f8 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java
@@ -24,25 +24,29 @@
import java.util.List;
/**
- * SearchResultsShim are a returned object from a query API.
+ * Encapsulates results of a search operation.
*
- * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets based
- * on request.
+ * <p>Each {@link AppSearchSessionShim#search} operation returns a list of {@link SearchResult}
+ * objects, referred to as a "page", limited by the size configured by {@link
+ * SearchSpec.Builder#setResultCountPerPage}.
*
- * <p>Should close this object after finish fetching results.
+ * <p>To fetch a page of results, call {@link #getNextPage()}.
+ *
+ * <p>All instances of {@link SearchResultsShim} must call {@link SearchResultsShim#close()} after
+ * the results are fetched.
*
* <p>This class is not thread safe.
*/
public interface SearchResultsShim extends Closeable {
/**
- * Gets a whole page of {@link SearchResult}s.
+ * Retrieves the next page of {@link SearchResult} objects.
*
- * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an empty
- * list.
+ * <p>The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}.
*
- * <p>The page size is set by {@link SearchSpec.Builder#setResultCountPerPage}.
+ * <p>Continue calling this method to access results until it returns an empty list, signifying
+ * there are no more results.
*
- * @return The pending result of performing this operation.
+ * @return a {@link ListenableFuture} which resolves to a list of {@link SearchResult} objects.
*/
@NonNull
ListenableFuture<List<SearchResult>> getNextPage();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index ac6eb32..fc2a409 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -151,6 +151,8 @@
private static final boolean ENFORCE_MAX_JOBS = true;
/** The maximum number of jobs that we allow an unprivileged app to schedule */
private static final int MAX_JOBS_PER_APP = 100;
+ /** The number of the most recently completed jobs to keep track of for debugging purposes. */
+ private static final int NUM_COMPLETED_JOB_HISTORY = 20;
@VisibleForTesting
public static Clock sSystemClock = Clock.systemUTC();
@@ -297,6 +299,10 @@
*/
boolean mReportedActive;
+ private int mLastCompletedJobIndex = 0;
+ private final JobStatus[] mLastCompletedJobs = new JobStatus[NUM_COMPLETED_JOB_HISTORY];
+ private final long[] mLastCompletedJobTimeElapsed = new long[NUM_COMPLETED_JOB_HISTORY];
+
/**
* A mapping of which uids are currently in the foreground to their effective priority.
*/
@@ -1752,6 +1758,10 @@
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
}
+ mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus;
+ mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis();
+ mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY;
+
// Intentionally not checking expedited job quota here. An app can't find out if it's run
// out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled
// EJ will just be demoted to a regular job if the app has no EJ quota left.
@@ -3298,6 +3308,37 @@
}
}
pw.decreaseIndent();
+
+ pw.println();
+ boolean recentPrinted = false;
+ pw.println("Recently completed jobs:");
+ pw.increaseIndent();
+ for (int r = 1; r <= NUM_COMPLETED_JOB_HISTORY; ++r) {
+ // Print most recent first
+ final int idx = (mLastCompletedJobIndex + NUM_COMPLETED_JOB_HISTORY - r)
+ % NUM_COMPLETED_JOB_HISTORY;
+ final JobStatus job = mLastCompletedJobs[idx];
+ if (job != null) {
+ if (!predicate.test(job)) {
+ continue;
+ }
+ recentPrinted = true;
+ TimeUtils.formatDuration(mLastCompletedJobTimeElapsed[idx], nowElapsed, pw);
+ pw.println();
+ // Double indent for readability
+ pw.increaseIndent();
+ pw.increaseIndent();
+ job.dump(pw, true, nowElapsed);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
+ }
+ if (!recentPrinted) {
+ pw.println("None");
+ }
+ pw.decreaseIndent();
+ pw.println();
+
if (filterUid == -1) {
pw.println();
pw.print("mReadyToRock="); pw.println(mReadyToRock);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 58396eb..a230b23 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -17,6 +17,7 @@
package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -59,6 +60,8 @@
private final AppStateTrackerImpl mAppStateTracker;
+ private final UpdateJobFunctor mUpdateJobFunctor = new UpdateJobFunctor();
+
public BackgroundJobsController(JobSchedulerService service) {
super(service);
@@ -69,7 +72,7 @@
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
- updateSingleJobRestrictionLocked(jobStatus, UNKNOWN);
+ updateSingleJobRestrictionLocked(jobStatus, sElapsedRealtimeClock.millis(), UNKNOWN);
}
@Override
@@ -79,7 +82,7 @@
@Override
public void evaluateStateLocked(JobStatus jobStatus) {
- updateSingleJobRestrictionLocked(jobStatus, UNKNOWN);
+ updateSingleJobRestrictionLocked(jobStatus, sElapsedRealtimeClock.millis(), UNKNOWN);
}
@Override
@@ -163,33 +166,34 @@
}
private void updateJobRestrictionsLocked(int filterUid, int newActiveState) {
- final UpdateJobFunctor updateTrackedJobs = new UpdateJobFunctor(newActiveState);
+ mUpdateJobFunctor.prepare(newActiveState);
final long start = DEBUG ? SystemClock.elapsedRealtimeNanos() : 0;
final JobStore store = mService.getJobStore();
if (filterUid > 0) {
- store.forEachJobForSourceUid(filterUid, updateTrackedJobs);
+ store.forEachJobForSourceUid(filterUid, mUpdateJobFunctor);
} else {
- store.forEachJob(updateTrackedJobs);
+ store.forEachJob(mUpdateJobFunctor);
}
final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0;
if (DEBUG) {
Slog.d(TAG, String.format(
"Job status updated: %d/%d checked/total jobs, %d us",
- updateTrackedJobs.mCheckedCount,
- updateTrackedJobs.mTotalCount,
+ mUpdateJobFunctor.mCheckedCount,
+ mUpdateJobFunctor.mTotalCount,
(time / 1000)
- ));
+ ));
}
- if (updateTrackedJobs.mChanged) {
+ if (mUpdateJobFunctor.mChanged) {
mStateChangedListener.onControllerStateChanged();
}
}
- boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, int activeState) {
+ boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, final long nowElapsed,
+ int activeState) {
final int uid = jobStatus.getSourceUid();
final String packageName = jobStatus.getSourcePackageName();
@@ -205,26 +209,32 @@
if (isActive && jobStatus.getStandbyBucket() == NEVER_INDEX) {
jobStatus.maybeLogBucketMismatch();
}
- boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
+ boolean didChange =
+ jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun);
didChange |= jobStatus.setUidActive(isActive);
return didChange;
}
private final class UpdateJobFunctor implements Consumer<JobStatus> {
- final int activeState;
+ int mActiveState;
boolean mChanged = false;
int mTotalCount = 0;
int mCheckedCount = 0;
+ long mUpdateTimeElapsed = 0;
- public UpdateJobFunctor(int newActiveState) {
- activeState = newActiveState;
+ void prepare(int newActiveState) {
+ mActiveState = newActiveState;
+ mUpdateTimeElapsed = sElapsedRealtimeClock.millis();
+ mChanged = false;
+ mTotalCount = 0;
+ mCheckedCount = 0;
}
@Override
public void accept(JobStatus jobStatus) {
mTotalCount++;
mCheckedCount++;
- if (updateSingleJobRestrictionLocked(jobStatus, activeState)) {
+ if (updateSingleJobRestrictionLocked(jobStatus, mUpdateTimeElapsed, mActiveState)) {
mChanged = true;
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 28269c8..6fd0948 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -65,10 +65,12 @@
@Override
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
if (taskStatus.hasPowerConstraint()) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
mTrackedTasks.add(taskStatus);
taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY);
- taskStatus.setChargingConstraintSatisfied(mChargeTracker.isOnStablePower());
- taskStatus.setBatteryNotLowConstraintSatisfied(mChargeTracker.isBatteryNotLow());
+ taskStatus.setChargingConstraintSatisfied(nowElapsed, mChargeTracker.isOnStablePower());
+ taskStatus.setBatteryNotLowConstraintSatisfied(
+ nowElapsed, mChargeTracker.isBatteryNotLow());
}
}
@@ -97,14 +99,15 @@
if (DEBUG) {
Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower);
}
+ final long nowElapsed = sElapsedRealtimeClock.millis();
boolean reportChange = false;
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
- boolean previous = ts.setChargingConstraintSatisfied(stablePower);
+ boolean previous = ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
if (previous != stablePower) {
reportChange = true;
}
- previous = ts.setBatteryNotLowConstraintSatisfied(batteryNotLow);
+ previous = ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
if (previous != batteryNotLow) {
reportChange = true;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 14484ff..6e542f3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -20,6 +20,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.annotation.Nullable;
import android.app.job.JobInfo;
@@ -461,11 +462,12 @@
final Network network = mConnManager.getActiveNetworkForUid(
jobStatus.getSourceUid(), jobStatus.shouldIgnoreNetworkBlocking());
final NetworkCapabilities capabilities = getNetworkCapabilities(network);
- return updateConstraintsSatisfied(jobStatus, network, capabilities);
+ return updateConstraintsSatisfied(jobStatus, sElapsedRealtimeClock.millis(),
+ network, capabilities);
}
- private boolean updateConstraintsSatisfied(JobStatus jobStatus, Network network,
- NetworkCapabilities capabilities) {
+ private boolean updateConstraintsSatisfied(JobStatus jobStatus, final long nowElapsed,
+ Network network, NetworkCapabilities capabilities) {
// TODO: consider matching against non-active networks
final boolean ignoreBlocked = jobStatus.shouldIgnoreNetworkBlocking();
@@ -476,7 +478,7 @@
final boolean satisfied = isSatisfied(jobStatus, network, capabilities, mConstants);
final boolean changed = jobStatus
- .setConnectivityConstraintSatisfied(connected && satisfied);
+ .setConnectivityConstraintSatisfied(nowElapsed, connected && satisfied);
// Pass along the evaluated network for job to use; prevents race
// conditions as default routes change over time, and opens the door to
@@ -530,6 +532,7 @@
NetworkCapabilities exemptedNetworkCapabilities = null;
boolean exemptedNetworkMatch = false;
+ final long nowElapsed = sElapsedRealtimeClock.millis();
boolean changed = false;
for (int i = jobs.size() - 1; i >= 0; i--) {
final JobStatus js = jobs.valueAt(i);
@@ -555,7 +558,7 @@
// job hasn't yet been evaluated against the currently
// active network; typically when we just lost a network.
if (match || !Objects.equals(js.network, net)) {
- changed |= updateConstraintsSatisfied(js, net, netCap);
+ changed |= updateConstraintsSatisfied(js, nowElapsed, net, netCap);
}
}
return changed;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
index 131a6d4..8b0da34 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
@@ -16,6 +16,8 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.annotation.UserIdInt;
import android.app.job.JobInfo;
import android.database.ContentObserver;
@@ -74,6 +76,7 @@
@Override
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
if (taskStatus.hasContentTriggerConstraint()) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
if (taskStatus.contentObserverJobInstance == null) {
taskStatus.contentObserverJobInstance = new JobInstance(taskStatus);
}
@@ -110,7 +113,7 @@
}
taskStatus.changedAuthorities = null;
taskStatus.changedUris = null;
- taskStatus.setContentTriggerConstraintSatisfied(havePendingUris);
+ taskStatus.setContentTriggerConstraintSatisfied(nowElapsed, havePendingUris);
}
if (lastJob != null && lastJob.contentObserverJobInstance != null) {
// And now we can detach the instance state from the last job.
@@ -295,7 +298,8 @@
boolean reportChange = false;
synchronized (mLock) {
if (mTriggerPending) {
- if (mJobStatus.setContentTriggerConstraintSatisfied(true)) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (mJobStatus.setContentTriggerConstraintSatisfied(nowElapsed, true)) {
reportChange = true;
}
unscheduleLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index 04b4164..192f5e6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -16,6 +16,8 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -104,8 +106,10 @@
+ Arrays.toString(mPowerSaveTempWhitelistAppIds));
}
boolean changed = false;
+ final long nowElapsed = sElapsedRealtimeClock.millis();
for (int i = 0; i < mAllowInIdleJobs.size(); i++) {
- changed |= updateTaskStateLocked(mAllowInIdleJobs.valueAt(i));
+ changed |=
+ updateTaskStateLocked(mAllowInIdleJobs.valueAt(i), nowElapsed);
}
if (changed) {
mStateChangedListener.onControllerStateChanged();
@@ -147,6 +151,7 @@
}
mDeviceIdleMode = enabled;
if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
+ mDeviceIdleUpdateFunctor.prepare();
if (enabled) {
mHandler.removeMessages(PROCESS_BACKGROUND_JOBS);
mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
@@ -180,7 +185,7 @@
Slog.d(TAG, "uid " + uid + " going " + (active ? "active" : "inactive"));
}
mForegroundUids.put(uid, active);
- mDeviceIdleUpdateFunctor.mChanged = false;
+ mDeviceIdleUpdateFunctor.prepare();
mService.getJobStore().forEachJobForSourceUid(uid, mDeviceIdleUpdateFunctor);
if (mDeviceIdleUpdateFunctor.mChanged) {
mStateChangedListener.onControllerStateChanged();
@@ -203,12 +208,12 @@
UserHandle.getAppId(job.getSourceUid()));
}
- private boolean updateTaskStateLocked(JobStatus task) {
+ private boolean updateTaskStateLocked(JobStatus task, final long nowElapsed) {
final boolean allowInIdle = ((task.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0)
&& (mForegroundUids.get(task.getSourceUid()) || isTempWhitelistedLocked(task));
final boolean whitelisted = isWhitelistedLocked(task);
final boolean enableTask = !mDeviceIdleMode || whitelisted || allowInIdle;
- return task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted);
+ return task.setDeviceNotDozingConstraintSatisfied(nowElapsed, enableTask, whitelisted);
}
@Override
@@ -216,7 +221,7 @@
if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
mAllowInIdleJobs.add(jobStatus);
}
- updateTaskStateLocked(jobStatus);
+ updateTaskStateLocked(jobStatus, sElapsedRealtimeClock.millis());
}
@Override
@@ -282,10 +287,16 @@
final class DeviceIdleUpdateFunctor implements Consumer<JobStatus> {
boolean mChanged;
+ long mUpdateTimeElapsed = 0;
+
+ void prepare() {
+ mChanged = false;
+ mUpdateTimeElapsed = sElapsedRealtimeClock.millis();
+ }
@Override
public void accept(JobStatus jobStatus) {
- mChanged |= updateTaskStateLocked(jobStatus);
+ mChanged |= updateTaskStateLocked(jobStatus, mUpdateTimeElapsed);
}
}
@@ -300,7 +311,7 @@
case PROCESS_BACKGROUND_JOBS:
// Just process all the jobs, the ones in foreground should already be running.
synchronized (mLock) {
- mDeviceIdleUpdateFunctor.mChanged = false;
+ mDeviceIdleUpdateFunctor.prepare();
mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
if (mDeviceIdleUpdateFunctor.mChanged) {
mStateChangedListener.onControllerStateChanged();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index 2fe827e..e26a3c6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -16,6 +16,8 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserHandle;
@@ -58,9 +60,10 @@
@Override
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
if (taskStatus.hasIdleConstraint()) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
mTrackedTasks.add(taskStatus);
taskStatus.setTrackingController(JobStatus.TRACKING_IDLE);
- taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle());
+ taskStatus.setIdleConstraintSatisfied(nowElapsed, mIdleTracker.isIdle());
}
}
@@ -90,8 +93,9 @@
@Override
public void reportNewIdleState(boolean isIdle) {
synchronized (mLock) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
- mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle);
+ mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle);
}
}
mStateChangedListener.onControllerStateChanged();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 6917fb5..5bdeb38 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -73,6 +73,8 @@
private static final String TAG = "JobScheduler.JobStatus";
static final boolean DEBUG = JobSchedulerService.DEBUG;
+ private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10;
+
public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
public static final long NO_EARLIEST_RUNTIME = 0L;
@@ -350,6 +352,10 @@
*/
private Pair<Long, Long> mPersistedUtcTimes;
+ private int mConstraintChangeHistoryIndex = 0;
+ private final long[] mConstraintUpdatedTimesElapsed = new long[NUM_CONSTRAINT_CHANGE_HISTORY];
+ private final int[] mConstraintStatusHistory = new int[NUM_CONSTRAINT_CHANGE_HISTORY];
+
/**
* For use only by ContentObserverController: state it is maintaining about content URIs
* being observed.
@@ -1090,28 +1096,28 @@
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setChargingConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
+ boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) {
+ return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state);
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setBatteryNotLowConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state);
+ boolean setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state) {
+ return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, nowElapsed, state);
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setStorageNotLowConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state);
+ boolean setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state) {
+ return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, nowElapsed, state);
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setTimingDelayConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
+ boolean setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state) {
+ return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, nowElapsed, state);
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setDeadlineConstraintSatisfied(boolean state) {
- if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) {
+ boolean setDeadlineConstraintSatisfied(final long nowElapsed, boolean state) {
+ if (setConstraintSatisfied(CONSTRAINT_DEADLINE, nowElapsed, state)) {
// The constraint was changed. Update the ready flag.
mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state;
return true;
@@ -1120,24 +1126,25 @@
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setIdleConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_IDLE, state);
+ boolean setIdleConstraintSatisfied(final long nowElapsed, boolean state) {
+ return setConstraintSatisfied(CONSTRAINT_IDLE, nowElapsed, state);
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setConnectivityConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
+ boolean setConnectivityConstraintSatisfied(final long nowElapsed, boolean state) {
+ return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, nowElapsed, state);
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setContentTriggerConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
+ boolean setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state) {
+ return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, nowElapsed, state);
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
+ boolean setDeviceNotDozingConstraintSatisfied(final long nowElapsed,
+ boolean state, boolean whitelisted) {
dozeWhitelisted = whitelisted;
- if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) {
+ if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, nowElapsed, state)) {
// The constraint was changed. Update the ready flag.
mReadyNotDozing = state || canRunInDoze();
return true;
@@ -1146,8 +1153,8 @@
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
- if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) {
+ boolean setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state) {
+ if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, nowElapsed, state)) {
// The constraint was changed. Update the ready flag.
mReadyNotRestrictedInBg = state;
return true;
@@ -1156,8 +1163,8 @@
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setQuotaConstraintSatisfied(boolean state) {
- if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, state)) {
+ boolean setQuotaConstraintSatisfied(final long nowElapsed, boolean state) {
+ if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, nowElapsed, state)) {
// The constraint was changed. Update the ready flag.
mReadyWithinQuota = state;
return true;
@@ -1166,8 +1173,8 @@
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setExpeditedJobQuotaConstraintSatisfied(boolean state) {
- if (setConstraintSatisfied(CONSTRAINT_WITHIN_EXPEDITED_QUOTA, state)) {
+ boolean setExpeditedJobQuotaConstraintSatisfied(final long nowElapsed, boolean state) {
+ if (setConstraintSatisfied(CONSTRAINT_WITHIN_EXPEDITED_QUOTA, nowElapsed, state)) {
// The constraint was changed. Update the ready flag.
mReadyWithinExpeditedQuota = state;
// DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag.
@@ -1190,7 +1197,7 @@
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setConstraintSatisfied(int constraint, boolean state) {
+ boolean setConstraintSatisfied(int constraint, final long nowElapsed, boolean state) {
boolean old = (satisfiedConstraints&constraint) != 0;
if (old == state) {
return false;
@@ -1212,6 +1219,12 @@
: FrameworkStatsLog
.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
}
+
+ mConstraintUpdatedTimesElapsed[mConstraintChangeHistoryIndex] = nowElapsed;
+ mConstraintStatusHistory[mConstraintChangeHistoryIndex] = satisfiedConstraints;
+ mConstraintChangeHistoryIndex =
+ (mConstraintChangeHistoryIndex + 1) % NUM_CONSTRAINT_CHANGE_HISTORY;
+
return true;
}
@@ -1700,7 +1713,7 @@
}
// Dumpsys infrastructure
- public void dump(IndentingPrintWriter pw, boolean full, long elapsedRealtimeMillis) {
+ public void dump(IndentingPrintWriter pw, boolean full, long nowElapsed) {
UserHandle.formatUid(pw, callingUid);
pw.print(" tag="); pw.println(tag);
@@ -1830,6 +1843,22 @@
dumpConstraints(pw,
((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
pw.println();
+
+ pw.println("Constraint history:");
+ pw.increaseIndent();
+ for (int h = 0; h < NUM_CONSTRAINT_CHANGE_HISTORY; ++h) {
+ final int idx = (h + mConstraintChangeHistoryIndex) % NUM_CONSTRAINT_CHANGE_HISTORY;
+ if (mConstraintUpdatedTimesElapsed[idx] == 0) {
+ continue;
+ }
+ TimeUtils.formatDuration(mConstraintUpdatedTimesElapsed[idx], nowElapsed, pw);
+ // dumpConstraints prepends with a space, so no need to add a space after the =
+ pw.print(" =");
+ dumpConstraints(pw, mConstraintStatusHistory[idx]);
+ pw.println();
+ }
+ pw.decreaseIndent();
+
if (dozeWhitelisted) {
pw.println("Doze whitelisted: true");
}
@@ -1910,26 +1939,25 @@
pw.increaseIndent();
if (whenStandbyDeferred != 0) {
pw.print("Deferred since: ");
- TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw);
+ TimeUtils.formatDuration(whenStandbyDeferred, nowElapsed, pw);
pw.println();
}
if (mFirstForceBatchedTimeElapsed != 0) {
pw.print("Time since first force batch attempt: ");
- TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, elapsedRealtimeMillis, pw);
+ TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, nowElapsed, pw);
pw.println();
}
pw.decreaseIndent();
pw.print("Enqueue time: ");
- TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw);
+ TimeUtils.formatDuration(enqueueTime, nowElapsed, pw);
pw.println();
pw.print("Run time: earliest=");
- formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis);
+ formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, nowElapsed);
pw.print(", latest=");
- formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis);
+ formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed);
pw.print(", original latest=");
- formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis,
- NO_LATEST_RUNTIME, elapsedRealtimeMillis);
+ formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed);
pw.println();
if (numFailures != 0) {
pw.print("Num failures: "); pw.println(numFailures);
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 2196b16..2faa836 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
@@ -626,6 +626,7 @@
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName);
@@ -636,11 +637,11 @@
jobs.add(jobStatus);
jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
final boolean isWithinQuota = isWithinQuotaLocked(jobStatus);
- setConstraintSatisfied(jobStatus, isWithinQuota);
+ setConstraintSatisfied(jobStatus, nowElapsed, isWithinQuota);
final boolean outOfEJQuota;
if (jobStatus.isRequestedExpeditedJob()) {
final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
- setExpeditedConstraintSatisfied(jobStatus, isWithinEJQuota);
+ setExpeditedConstraintSatisfied(jobStatus, nowElapsed, isWithinEJQuota);
outOfEJQuota = !isWithinEJQuota;
} else {
outOfEJQuota = false;
@@ -827,8 +828,8 @@
}
@NonNull
- private ShrinkableDebits getEJQuotaLocked(final int userId,
- @NonNull final String packageName) {
+ @VisibleForTesting
+ ShrinkableDebits getEJDebitsLocked(final int userId, @NonNull final String packageName) {
ShrinkableDebits debits = mEJStats.get(userId, packageName);
if (debits == null) {
debits = new ShrinkableDebits(
@@ -930,7 +931,7 @@
@VisibleForTesting
long getRemainingEJExecutionTimeLocked(final int userId, @NonNull final String packageName) {
- ShrinkableDebits quota = getEJQuotaLocked(userId, packageName);
+ ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
if (quota.getStandbyBucketLocked() == NEVER_INDEX) {
return 0;
}
@@ -947,7 +948,7 @@
if (ts.endTimeElapsed < windowStartTimeElapsed) {
final long duration = ts.endTimeElapsed - ts.startTimeElapsed;
remainingMs += duration;
- quota.transactOnDebitsLocked(-duration);
+ quota.transactLocked(-duration);
timingSessions.remove(0);
} else if (ts.startTimeElapsed < windowStartTimeElapsed) {
remainingMs += windowStartTimeElapsed - ts.startTimeElapsed;
@@ -959,15 +960,16 @@
}
}
+ TopAppTimer topAppTimer = mTopAppTrackers.get(userId, packageName);
+ if (topAppTimer != null && topAppTimer.isActive()) {
+ remainingMs += topAppTimer.getPendingReward(nowElapsed);
+ }
+
Timer timer = mEJPkgTimers.get(userId, packageName);
if (timer == null) {
return remainingMs;
}
- // There's a case where the debits tally is 0 but a currently running HPJ still counts
- // towards quota. If the app gets a reward in this case, the reward is lost and the HPJ
- // run is still fully counted.
- // TODO(171305774)/STOPSHIP: make sure getting rewards while HPJ currently executing isn't
- // treated negatively
+
return remainingMs - timer.getCurrentDuration(sElapsedRealtimeClock.millis());
}
@@ -1080,7 +1082,7 @@
}
final long nowElapsed = sElapsedRealtimeClock.millis();
- ShrinkableDebits quota = getEJQuotaLocked(userId, packageName);
+ ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked());
final long startWindowElapsed = Math.max(0, nowElapsed - mEJLimitWindowSizeMs);
long remainingDeadSpaceMs = remainingExecutionTimeMs;
@@ -1371,6 +1373,11 @@
@VisibleForTesting
void saveTimingSession(final int userId, @NonNull final String packageName,
@NonNull final TimingSession session, boolean isExpedited) {
+ saveTimingSession(userId, packageName, session, isExpedited, 0);
+ }
+
+ private void saveTimingSession(final int userId, @NonNull final String packageName,
+ @NonNull final TimingSession session, boolean isExpedited, long debitAdjustment) {
synchronized (mLock) {
final SparseArrayMap<String, List<TimingSession>> sessionMap =
isExpedited ? mEJTimingSessions : mTimingSessions;
@@ -1381,8 +1388,9 @@
}
sessions.add(session);
if (isExpedited) {
- final ShrinkableDebits quota = getEJQuotaLocked(userId, packageName);
- quota.transactOnDebitsLocked(session.endTimeElapsed - session.startTimeElapsed);
+ final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
+ quota.transactLocked(session.endTimeElapsed - session.startTimeElapsed
+ + debitAdjustment);
} else {
// Adding a new session means that the current stats are now incorrect.
invalidateAllExecutionStatsLocked(userId, packageName);
@@ -1395,14 +1403,34 @@
private void grantRewardForInstantEvent(
final int userId, @NonNull final String packageName, final long credit) {
synchronized (mLock) {
- final ShrinkableDebits quota = getEJQuotaLocked(userId, packageName);
- quota.transactOnDebitsLocked(-credit);
- if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
+ if (transactQuotaLocked(userId, packageName, nowElapsed, quota, credit)
+ && maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) {
mStateChangedListener.onControllerStateChanged();
}
}
}
+ private boolean transactQuotaLocked(final int userId, @NonNull final String packageName,
+ final long nowElapsed, @NonNull ShrinkableDebits debits, final long credit) {
+ final long oldTally = debits.getTallyLocked();
+ final long leftover = debits.transactLocked(-credit);
+ if (DEBUG) {
+ Slog.d(TAG, "debits overflowed by " + leftover);
+ }
+ boolean changed = oldTally != debits.getTallyLocked();
+ if (leftover != 0) {
+ // Only adjust timer if its active.
+ final Timer ejTimer = mEJPkgTimers.get(userId, packageName);
+ if (ejTimer != null && ejTimer.isActive()) {
+ ejTimer.updateDebitAdjustment(nowElapsed, leftover);
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
private final class EarliestEndTimeFunctor implements Consumer<List<TimingSession>> {
public long earliestEndElapsed = Long.MAX_VALUE;
@@ -1499,11 +1527,12 @@
private void maybeUpdateAllConstraintsLocked() {
boolean changed = false;
+ final long nowElapsed = sElapsedRealtimeClock.millis();
for (int u = 0; u < mTrackedJobs.numMaps(); ++u) {
final int userId = mTrackedJobs.keyAt(u);
for (int p = 0; p < mTrackedJobs.numElementsForKey(userId); ++p) {
final String packageName = mTrackedJobs.keyAt(u, p);
- changed |= maybeUpdateConstraintForPkgLocked(userId, packageName);
+ changed |= maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName);
}
}
if (changed) {
@@ -1516,7 +1545,7 @@
*
* @return true if at least one job had its bit changed
*/
- private boolean maybeUpdateConstraintForPkgLocked(final int userId,
+ private boolean maybeUpdateConstraintForPkgLocked(final long nowElapsed, final int userId,
@NonNull final String packageName) {
ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
if (jobs == null || jobs.size() == 0) {
@@ -1533,21 +1562,21 @@
if (isTopStartedJobLocked(js)) {
// Job was started while the app was in the TOP state so we should allow it to
// finish.
- changed |= js.setQuotaConstraintSatisfied(true);
+ changed |= js.setQuotaConstraintSatisfied(nowElapsed, true);
} else if (realStandbyBucket != ACTIVE_INDEX
&& realStandbyBucket == js.getEffectiveStandbyBucket()) {
// An app in the ACTIVE bucket may be out of quota while the job could be in quota
// for some reason. Therefore, avoid setting the real value here and check each job
// individually.
- changed |= setConstraintSatisfied(js, realInQuota);
+ changed |= setConstraintSatisfied(js, nowElapsed, realInQuota);
} else {
// This job is somehow exempted. Need to determine its own quota status.
- changed |= setConstraintSatisfied(js, isWithinQuotaLocked(js));
+ changed |= setConstraintSatisfied(js, nowElapsed, isWithinQuotaLocked(js));
}
if (js.isRequestedExpeditedJob()) {
boolean isWithinEJQuota = isWithinEJQuotaLocked(js);
- changed |= setExpeditedConstraintSatisfied(js, isWithinEJQuota);
+ changed |= setExpeditedConstraintSatisfied(js, nowElapsed, isWithinEJQuota);
outOfEJQuota |= !isWithinEJQuota;
}
}
@@ -1566,14 +1595,21 @@
private final SparseArrayMap<String, Integer> mToScheduleStartAlarms =
new SparseArrayMap<>();
public boolean wasJobChanged;
+ long mUpdateTimeElapsed = 0;
+
+ void prepare() {
+ mUpdateTimeElapsed = sElapsedRealtimeClock.millis();
+ }
@Override
public void accept(JobStatus jobStatus) {
- wasJobChanged |= setConstraintSatisfied(jobStatus, isWithinQuotaLocked(jobStatus));
+ wasJobChanged |= setConstraintSatisfied(
+ jobStatus, mUpdateTimeElapsed, isWithinQuotaLocked(jobStatus));
final boolean outOfEJQuota;
if (jobStatus.isRequestedExpeditedJob()) {
final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
- wasJobChanged |= setExpeditedConstraintSatisfied(jobStatus, isWithinEJQuota);
+ wasJobChanged |= setExpeditedConstraintSatisfied(
+ jobStatus, mUpdateTimeElapsed, isWithinEJQuota);
outOfEJQuota = !isWithinEJQuota;
} else {
outOfEJQuota = false;
@@ -1611,6 +1647,7 @@
private final UidConstraintUpdater mUpdateUidConstraints = new UidConstraintUpdater();
private boolean maybeUpdateConstraintForUidLocked(final int uid) {
+ mUpdateUidConstraints.prepare();
mService.getJobStore().forEachJobForSourceUid(uid, mUpdateUidConstraints);
mUpdateUidConstraints.postProcess();
@@ -1716,21 +1753,22 @@
mInQuotaAlarmListener.addAlarmLocked(userId, packageName, inQuotaTimeElapsed);
}
- private boolean setConstraintSatisfied(@NonNull JobStatus jobStatus, boolean isWithinQuota) {
+ private boolean setConstraintSatisfied(@NonNull JobStatus jobStatus, long nowElapsed,
+ boolean isWithinQuota) {
if (!isWithinQuota && jobStatus.getWhenStandbyDeferred() == 0) {
// Mark that the job is being deferred due to buckets.
- jobStatus.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
+ jobStatus.setWhenStandbyDeferred(nowElapsed);
}
- return jobStatus.setQuotaConstraintSatisfied(isWithinQuota);
+ return jobStatus.setQuotaConstraintSatisfied(nowElapsed, isWithinQuota);
}
/**
* If the satisfaction changes, this will tell connectivity & background jobs controller to
* also re-evaluate their state.
*/
- private boolean setExpeditedConstraintSatisfied(@NonNull JobStatus jobStatus,
+ private boolean setExpeditedConstraintSatisfied(@NonNull JobStatus jobStatus, long nowElapsed,
boolean isWithinQuota) {
- if (jobStatus.setExpeditedJobQuotaConstraintSatisfied(isWithinQuota)) {
+ if (jobStatus.setExpeditedJobQuotaConstraintSatisfied(nowElapsed, isWithinQuota)) {
mBackgroundJobsController.evaluateStateLocked(jobStatus);
mConnectivityController.evaluateStateLocked(jobStatus);
if (isWithinQuota && jobStatus.isReady()) {
@@ -1863,7 +1901,8 @@
}
}
- private static final class ShrinkableDebits {
+ @VisibleForTesting
+ static final class ShrinkableDebits {
/** The amount of quota remaining. Can be negative if limit changes. */
private long mDebitTally;
private int mStandbyBucket;
@@ -1881,8 +1920,11 @@
* Negative if the tally should decrease (therefore increasing available quota);
* or positive if the tally should increase (therefore decreasing available quota).
*/
- void transactOnDebitsLocked(final long amount) {
+ long transactLocked(final long amount) {
+ final long leftover = amount < 0 && Math.abs(amount) > mDebitTally
+ ? mDebitTally + amount : 0;
mDebitTally = Math.max(0, mDebitTally + amount);
+ return leftover;
}
void setStandbyBucketLocked(int standbyBucket) {
@@ -1915,6 +1957,7 @@
private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>();
private long mStartTimeElapsed;
private int mBgJobCount;
+ private long mDebitAdjustment;
Timer(int uid, int userId, String packageName, boolean regularJobTimer) {
mPkg = new Package(userId, packageName);
@@ -1945,6 +1988,7 @@
if (mRunningBgJobs.size() == 1) {
// Started tracking the first job.
mStartTimeElapsed = sElapsedRealtimeClock.millis();
+ mDebitAdjustment = 0;
if (mRegularJobTimer) {
// Starting the timer means that all cached execution stats are now
// incorrect.
@@ -1976,6 +2020,11 @@
}
}
+ void updateDebitAdjustment(long nowElapsed, long debit) {
+ // Make sure we don't have a credit larger than the expected session.
+ mDebitAdjustment = Math.max(mDebitAdjustment + debit, mStartTimeElapsed - nowElapsed);
+ }
+
/**
* Stops tracking all jobs and cancels any pending alarms. This should only be called if
* the Timer is not going to be used anymore.
@@ -1991,7 +2040,8 @@
return;
}
TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mBgJobCount);
- saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer);
+ saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer,
+ mDebitAdjustment);
mBgJobCount = 0;
// Don't reset the tracked jobs list as we need to keep tracking the current number
// of jobs.
@@ -2018,7 +2068,7 @@
long getCurrentDuration(long nowElapsed) {
synchronized (mLock) {
- return !isActive() ? 0 : nowElapsed - mStartTimeElapsed;
+ return !isActive() ? 0 : nowElapsed - mStartTimeElapsed + mDebitAdjustment;
}
}
@@ -2047,6 +2097,7 @@
// Start timing from unplug.
if (mRunningBgJobs.size() > 0) {
mStartTimeElapsed = nowElapsed;
+ mDebitAdjustment = 0;
// NOTE: this does have the unfortunate consequence that if the device is
// repeatedly plugged in and unplugged, or an app changes foreground state
// very frequently, the job count for a package may be artificially high.
@@ -2116,6 +2167,11 @@
pw.print(", ");
pw.print(mBgJobCount);
pw.print(" running bg jobs");
+ if (!mRegularJobTimer) {
+ pw.print(" (debit adj=");
+ pw.print(mDebitAdjustment);
+ pw.print(")");
+ }
pw.println();
pw.increaseIndent();
for (int i = 0; i < mRunningBgJobs.size(); i++) {
@@ -2159,6 +2215,21 @@
mPkg = new Package(userId, packageName);
}
+ private int calculateTimeChunks(final long nowElapsed) {
+ final long totalTopTimeMs = nowElapsed - mStartTimeElapsed;
+ int numTimeChunks = (int) (totalTopTimeMs / mEJTopAppTimeChunkSizeMs);
+ final long remainderMs = totalTopTimeMs % mEJTopAppTimeChunkSizeMs;
+ if (remainderMs >= SECOND_IN_MILLIS) {
+ // "Round up"
+ numTimeChunks++;
+ }
+ return numTimeChunks;
+ }
+
+ long getPendingReward(final long nowElapsed) {
+ return mEJRewardTopAppMs * calculateTimeChunks(nowElapsed);
+ }
+
void processEventLocked(@NonNull UsageEvents.Event event) {
final long nowElapsed = sElapsedRealtimeClock.millis();
switch (event.getEventType()) {
@@ -2174,21 +2245,17 @@
final UsageEvents.Event existingEvent =
mActivities.removeReturnOld(event.mInstanceId);
if (existingEvent != null && mActivities.size() == 0) {
- final long totalTopTimeMs = nowElapsed - mStartTimeElapsed;
- int numTimeChunks = (int) (totalTopTimeMs / mEJTopAppTimeChunkSizeMs);
- final long remainderMs = totalTopTimeMs % mEJTopAppTimeChunkSizeMs;
- if (remainderMs >= SECOND_IN_MILLIS) {
- // "Round up"
- numTimeChunks++;
- }
+ final long pendingReward = getPendingReward(nowElapsed);
if (DEBUG) {
- Slog.d(TAG,
- "Crediting " + mPkg + " for " + numTimeChunks + " time chunks");
+ Slog.d(TAG, "Crediting " + mPkg + " " + pendingReward + "ms"
+ + " for " + calculateTimeChunks(nowElapsed) + " time chunks");
}
- final ShrinkableDebits quota =
- getEJQuotaLocked(mPkg.userId, mPkg.packageName);
- quota.transactOnDebitsLocked(-mEJRewardTopAppMs * numTimeChunks);
- if (maybeUpdateConstraintForPkgLocked(mPkg.userId, mPkg.packageName)) {
+ final ShrinkableDebits debits =
+ getEJDebitsLocked(mPkg.userId, mPkg.packageName);
+ if (transactQuotaLocked(mPkg.userId, mPkg.packageName,
+ nowElapsed, debits, pendingReward)
+ && maybeUpdateConstraintForPkgLocked(nowElapsed,
+ mPkg.userId, mPkg.packageName)) {
mStateChangedListener.onControllerStateChanged();
}
}
@@ -2292,7 +2359,8 @@
if (timer != null && timer.isActive()) {
timer.rescheduleCutoff();
}
- if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
+ if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
+ userId, packageName)) {
mStateChangedListener.onControllerStateChanged();
}
}
@@ -2307,7 +2375,7 @@
*/
@Override
public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) {
- mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event);
+ mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event).sendToTarget();
}
}
@@ -2394,7 +2462,6 @@
}
private class QcHandler extends Handler {
- private boolean mIsProcessing;
QcHandler(Looper looper) {
super(looper);
@@ -2403,8 +2470,6 @@
@Override
public void handleMessage(Message msg) {
synchronized (mLock) {
- mIsProcessing = true;
-
switch (msg.what) {
case MSG_REACHED_QUOTA: {
Package pkg = (Package) msg.obj;
@@ -2417,7 +2482,8 @@
if (timeRemainingMs <= 50) {
// Less than 50 milliseconds left. Start process of shutting down jobs.
if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
- if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) {
+ if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
+ pkg.userId, pkg.packageName)) {
mStateChangedListener.onControllerStateChanged();
}
} else {
@@ -2444,7 +2510,8 @@
pkg.userId, pkg.packageName);
if (timeRemainingMs <= 0) {
if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
- if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) {
+ if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
+ pkg.userId, pkg.packageName)) {
mStateChangedListener.onControllerStateChanged();
}
} else {
@@ -2475,7 +2542,8 @@
if (DEBUG) {
Slog.d(TAG, "Checking pkg " + string(userId, packageName));
}
- if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
+ if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
+ userId, packageName)) {
mStateChangedListener.onControllerStateChanged();
}
break;
@@ -2522,6 +2590,10 @@
final int userId = msg.arg1;
final UsageEvents.Event event = (UsageEvents.Event) msg.obj;
final String pkgName = event.getPackageName();
+ if (DEBUG) {
+ Slog.d(TAG, "Processing event " + event.getEventType()
+ + " for " + string(userId, pkgName));
+ }
switch (event.getEventType()) {
case UsageEvents.Event.ACTIVITY_RESUMED:
case UsageEvents.Event.ACTIVITY_PAUSED:
@@ -2587,8 +2659,6 @@
}
}
}
-
- mIsProcessing = false;
}
}
@@ -3866,11 +3936,6 @@
return mQcConstants;
}
- @VisibleForTesting
- boolean isActiveBackgroundProcessing() {
- return mHandler.mIsProcessing;
- }
-
//////////////////////////// DATA DUMP //////////////////////////////
@Override
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
index 0731918..8678913 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
@@ -61,9 +61,11 @@
@Override
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
if (taskStatus.hasStorageNotLowConstraint()) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
mTrackedTasks.add(taskStatus);
taskStatus.setTrackingController(JobStatus.TRACKING_STORAGE);
- taskStatus.setStorageNotLowConstraintSatisfied(mStorageTracker.isStorageNotLow());
+ taskStatus.setStorageNotLowConstraintSatisfied(
+ nowElapsed, mStorageTracker.isStorageNotLow());
}
}
@@ -76,12 +78,13 @@
}
private void maybeReportNewStorageState() {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
final boolean storageNotLow = mStorageTracker.isStorageNotLow();
boolean reportChange = false;
synchronized (mLock) {
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
- reportChange |= ts.setStorageNotLowConstraintSatisfied(storageNotLow);
+ reportChange |= ts.setStorageNotLowConstraintSatisfied(nowElapsed, storageNotLow);
}
}
if (storageNotLow) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index ede14ec..e8ebfb5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -258,9 +258,9 @@
if (jobDeadline <= nowElapsedMillis) {
if (job.hasTimingDelayConstraint()) {
- job.setTimingDelayConstraintSatisfied(true);
+ job.setTimingDelayConstraintSatisfied(nowElapsedMillis, true);
}
- job.setDeadlineConstraintSatisfied(true);
+ job.setDeadlineConstraintSatisfied(nowElapsedMillis, true);
return true;
}
return false;
@@ -332,7 +332,7 @@
private boolean evaluateTimingDelayConstraint(JobStatus job, long nowElapsedMillis) {
final long jobDelayTime = job.getEarliestRunTime();
if (jobDelayTime <= nowElapsedMillis) {
- job.setTimingDelayConstraintSatisfied(true);
+ job.setTimingDelayConstraintSatisfied(nowElapsedMillis, true);
return true;
}
return false;
diff --git a/apex/jobscheduler/service/jni/Android.bp b/apex/jobscheduler/service/jni/Android.bp
index 4bcc165..c630217 100644
--- a/apex/jobscheduler/service/jni/Android.bp
+++ b/apex/jobscheduler/service/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libalarm_jni",
diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp
index 5b24cfa..9b3399e 100644
--- a/apex/media/service/Android.bp
+++ b/apex/media/service/Android.bp
@@ -11,6 +11,15 @@
// WITHOUT 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "service-media-s-sources",
srcs: [
@@ -38,4 +47,3 @@
"com.android.media",
],
}
-
diff --git a/cmds/abx/Android.bp b/cmds/abx/Android.bp
index 333aced..50a0b75 100644
--- a/cmds/abx/Android.bp
+++ b/cmds/abx/Android.bp
@@ -1,4 +1,21 @@
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_abx_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_abx_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "abx",
wrapper: "abx",
diff --git a/cmds/hid/OWNERS b/cmds/hid/OWNERS
new file mode 100644
index 0000000..d701f23
--- /dev/null
+++ b/cmds/hid/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/hardware/input/OWNERS
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 437a87e..422c2be 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -113,9 +113,10 @@
checkAndClearException(env, "onDeviceGetReport");
}
-void DeviceCallback::onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data) {
+void DeviceCallback::onDeviceOutput(uint8_t eventId, uint8_t rType,
+ const std::vector<uint8_t>& data) {
JNIEnv* env = getJNIEnv();
- env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, rType,
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, eventId, rType,
toJbyteArray(env, data).get());
checkAndClearException(env, "onDeviceOutput");
}
@@ -261,6 +262,7 @@
ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
set_report.rnum, toString(data).c_str());
}
+ mDeviceCallback->onDeviceOutput(UHID_SET_REPORT, set_report.rtype, data);
break;
}
case UHID_OUTPUT: {
@@ -269,7 +271,7 @@
if (DEBUG_OUTPUT) {
ALOGD("UHID_OUTPUT rtype=%" PRIu8 " data=%s", output.rtype, toString(data).c_str());
}
- mDeviceCallback->onDeviceOutput(output.rtype, data);
+ mDeviceCallback->onDeviceOutput(UHID_OUTPUT, output.rtype, data);
break;
}
default: {
@@ -365,7 +367,7 @@
uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
uhid::gDeviceCallbackClassInfo.onDeviceOutput =
- env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V");
+ env->GetMethodID(clazz, "onDeviceOutput", "(BB[B)V");
uhid::gDeviceCallbackClassInfo.onDeviceError =
env->GetMethodID(clazz, "onDeviceError", "()V");
if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 5483b40..bb73132 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -31,7 +31,7 @@
void onDeviceOpen();
void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
- void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data);
+ void onDeviceOutput(uint8_t eventId, uint8_t rType, const std::vector<uint8_t>& data);
void onDeviceError();
private:
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 20b4bd8..37d0b1c 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -46,7 +46,8 @@
// Sync with linux uhid_event_type::UHID_OUTPUT
private static final byte UHID_EVENT_TYPE_UHID_OUTPUT = 6;
-
+ // Sync with linux uhid_event_type::UHID_SET_REPORT
+ private static final byte UHID_EVENT_TYPE_SET_REPORT = 13;
private final int mId;
private final HandlerThread mThread;
private final DeviceHandler mHandler;
@@ -199,10 +200,10 @@
}
// native callback
- public void onDeviceOutput(byte rtype, byte[] data) {
+ public void onDeviceOutput(byte eventId, byte rtype, byte[] data) {
JSONObject json = new JSONObject();
try {
- json.put("eventId", UHID_EVENT_TYPE_UHID_OUTPUT);
+ json.put("eventId", eventId);
json.put("deviceId", mId);
json.put("reportType", rtype);
JSONArray dataArray = new JSONArray();
diff --git a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
index a0361d0..ca9df39 100644
--- a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
+++ b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
@@ -182,6 +182,8 @@
mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
} else if (opt.equals("--rc") || opt.equals("--require-charging")) {
mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, true);
+ } else if (opt.equals("--ej") || opt.equals("--schedule-as-ej")) {
+ mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true);
} else if (opt.equals("-e") || opt.equals("--es") || opt.equals("--extra-string")) {
final String key = nextArgRequired();
final String value = nextArgRequired();
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 5c08704..d4da5e5 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -34,7 +34,6 @@
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
-#include <ui/DisplayInfo.h>
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp
index 0d7fed2..260cfc7 100644
--- a/cmds/uinput/Android.bp
+++ b/cmds/uinput/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2020 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_uinput_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_uinput_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "uinput",
wrapper: "uinput",
@@ -15,4 +32,4 @@
srcs: [
"src/com/android/commands/uinput/InputAbsInfo.aidl",
],
-}
\ No newline at end of file
+}
diff --git a/cmds/uinput/jni/Android.bp b/cmds/uinput/jni/Android.bp
index 199bbbd..c56adc3 100644
--- a/cmds/uinput/jni/Android.bp
+++ b/cmds/uinput/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_cmds_uinput_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_cmds_uinput_license"],
+}
+
cc_library_shared {
name: "libuinputcommand_jni",
diff --git a/core/api/current.txt b/core/api/current.txt
index d6c783d..160ad7a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -528,6 +528,8 @@
field public static final int detailSocialSummary = 16843428; // 0x10102a4
field public static final int detailsElementBackground = 16843598; // 0x101034e
field public static final int dial = 16843010; // 0x1010102
+ field public static final int dialTint = 16844342; // 0x1010636
+ field public static final int dialTintMode = 16844343; // 0x1010637
field public static final int dialogCornerRadius = 16844145; // 0x1010571
field public static final int dialogIcon = 16843252; // 0x10101f4
field public static final int dialogLayout = 16843255; // 0x10101f7
@@ -725,8 +727,14 @@
field public static final int groupIndicator = 16843019; // 0x101010b
field public static final int gwpAsanMode = 16844310; // 0x1010616
field public static final int hand_hour = 16843011; // 0x1010103
+ field public static final int hand_hourTint = 16844344; // 0x1010638
+ field public static final int hand_hourTintMode = 16844345; // 0x1010639
field public static final int hand_minute = 16843012; // 0x1010104
+ field public static final int hand_minuteTint = 16844346; // 0x101063a
+ field public static final int hand_minuteTintMode = 16844347; // 0x101063b
field public static final int hand_second = 16844323; // 0x1010623
+ field public static final int hand_secondTint = 16844348; // 0x101063c
+ field public static final int hand_secondTintMode = 16844349; // 0x101063d
field public static final int handle = 16843354; // 0x101025a
field public static final int handleProfiling = 16842786; // 0x1010022
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
@@ -4735,6 +4743,13 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
}
+ public final class BackgroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable {
+ ctor public BackgroundServiceStartNotAllowedException(@NonNull String);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.BackgroundServiceStartNotAllowedException> CREATOR;
+ }
+
public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
ctor public DatePickerDialog(@NonNull android.content.Context);
ctor public DatePickerDialog(@NonNull android.content.Context, @StyleRes int);
@@ -4980,6 +4995,13 @@
method @Deprecated public void setSelectedGroup(int);
}
+ public final class ForegroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable {
+ ctor public ForegroundServiceStartNotAllowedException(@NonNull String);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ForegroundServiceStartNotAllowedException> CREATOR;
+ }
+
@Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
ctor @Deprecated public Fragment();
method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]);
@@ -5588,6 +5610,7 @@
field public static final int DEFAULT_LIGHTS = 4; // 0x4
field public static final int DEFAULT_SOUND = 1; // 0x1
field public static final int DEFAULT_VIBRATE = 2; // 0x2
+ field public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
@@ -5599,6 +5622,7 @@
field public static final String EXTRA_COLORIZED = "android.colorized";
field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+ field public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
@@ -5886,6 +5910,8 @@
method @NonNull public static android.app.Notification.CallStyle forIncomingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
method @NonNull public static android.app.Notification.CallStyle forOngoingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent);
method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
+ method @NonNull public android.app.Notification.CallStyle setAnswerButtonColorHint(@ColorInt int);
+ method @NonNull public android.app.Notification.CallStyle setDeclineButtonColorHint(@ColorInt int);
method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon);
method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence);
}
@@ -6563,6 +6589,9 @@
field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
}
+ public abstract class ServiceStartNotAllowedException extends java.lang.IllegalStateException {
+ }
+
public abstract class SharedElementCallback {
ctor public SharedElementCallback();
method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF);
@@ -8346,7 +8375,9 @@
method protected android.view.View getDefaultView();
method protected android.view.View getErrorView();
method protected void prepareView(android.view.View);
+ method public void resetColorResources();
method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
+ method public void setColorResources(@NonNull android.util.SparseIntArray);
method public void setCurrentSize(@NonNull android.graphics.PointF);
method public void setExecutor(java.util.concurrent.Executor);
method public void setOnLightBackground(boolean);
@@ -8427,7 +8458,7 @@
method public int describeContents();
method public final android.os.UserHandle getProfile();
method @NonNull public android.content.pm.ActivityInfo getProviderInfo();
- method @Nullable public final String loadDescription(@NonNull android.content.Context);
+ method @Nullable public final CharSequence loadDescription(@NonNull android.content.Context);
method public final android.graphics.drawable.Drawable loadIcon(@NonNull android.content.Context, int);
method public final String loadLabel(android.content.pm.PackageManager);
method public final android.graphics.drawable.Drawable loadPreviewImage(@NonNull android.content.Context, int);
@@ -8445,7 +8476,7 @@
field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1
field public int autoAdvanceViewId;
field public android.content.ComponentName configure;
- field @IdRes public int descriptionResource;
+ field @IdRes public int descriptionRes;
field public int icon;
field public int initialKeyguardLayout;
field public int initialLayout;
@@ -10213,6 +10244,7 @@
field public static final String SYNC_EXTRAS_MANUAL = "force";
field public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
field public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
+ field public static final String SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB = "schedule_as_expedited_job";
field public static final String SYNC_EXTRAS_UPLOAD = "upload";
field public static final int SYNC_OBSERVER_TYPE_ACTIVE = 4; // 0x4
field public static final int SYNC_OBSERVER_TYPE_PENDING = 2; // 0x2
@@ -11535,6 +11567,7 @@
method public android.content.SyncRequest.Builder setManual(boolean);
method public android.content.SyncRequest.Builder setNoRetry(boolean);
method public android.content.SyncRequest.Builder setRequiresCharging(boolean);
+ method @NonNull public android.content.SyncRequest.Builder setScheduleAsExpeditedJob(boolean);
method public android.content.SyncRequest.Builder setSyncAdapter(android.accounts.Account, String);
method public android.content.SyncRequest.Builder syncOnce();
method public android.content.SyncRequest.Builder syncPeriodic(long, long);
@@ -11786,6 +11819,8 @@
field public int category;
field public String className;
field public int compatibleWidthLimitDp;
+ field public int compileSdkVersion;
+ field @Nullable public String compileSdkVersionCodename;
field public String dataDir;
field public int descriptionRes;
field public String deviceProtectedDataDir;
@@ -14615,11 +14650,6 @@
enum_constant public static final android.graphics.BlurMaskFilter.Blur SOLID;
}
- public final class BlurShader extends android.graphics.Shader {
- ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
- ctor public BlurShader(float, float, @Nullable android.graphics.Shader, @NonNull android.graphics.Shader.TileMode);
- }
-
public class Camera {
ctor public Camera();
method public void applyToCanvas(android.graphics.Canvas);
@@ -18645,6 +18675,58 @@
}
+package android.hardware.lights {
+
+ public final class Light implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getId();
+ method @NonNull public String getName();
+ method public int getOrdinal();
+ method public int getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
+ field public static final int LIGHT_TYPE_INPUT_PLAYER_ID = 10; // 0xa
+ field public static final int LIGHT_TYPE_INPUT_RGB = 11; // 0xb
+ field public static final int LIGHT_TYPE_INPUT_SINGLE = 9; // 0x9
+ field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
+ }
+
+ public final class LightState implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public static android.hardware.lights.LightState forColor(@ColorInt int);
+ method @NonNull public static android.hardware.lights.LightState forPlayerId(int);
+ method @ColorInt public int getColor();
+ method public int getPlayerId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR;
+ }
+
+ public abstract class LightsManager {
+ method @NonNull public abstract android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light);
+ method @NonNull public abstract java.util.List<android.hardware.lights.Light> getLights();
+ method @NonNull public abstract android.hardware.lights.LightsManager.LightsSession openSession();
+ }
+
+ public abstract static class LightsManager.LightsSession implements java.lang.AutoCloseable {
+ ctor public LightsManager.LightsSession();
+ method public abstract void close();
+ method public abstract void requestLights(@NonNull android.hardware.lights.LightsRequest);
+ }
+
+ public final class LightsRequest {
+ method @NonNull public java.util.List<android.hardware.lights.LightState> getLightStates();
+ method @NonNull public java.util.List<java.lang.Integer> getLights();
+ }
+
+ public static final class LightsRequest.Builder {
+ ctor public LightsRequest.Builder();
+ method @NonNull public android.hardware.lights.LightsRequest.Builder addLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+ method @NonNull public android.hardware.lights.LightsRequest build();
+ method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light);
+ }
+
+}
+
package android.hardware.usb {
public class UsbAccessory implements android.os.Parcelable {
@@ -26205,6 +26287,7 @@
field public static final int NET_CAPABILITY_CBS = 5; // 0x5
field public static final int NET_CAPABILITY_DUN = 2; // 0x2
field public static final int NET_CAPABILITY_EIMS = 10; // 0xa
+ field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d
field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
field public static final int NET_CAPABILITY_IA = 7; // 0x7
@@ -30572,8 +30655,8 @@
}
public final class BugreportManager {
- method public void cancelBugreport();
- method public void startConnectivityBugreport(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
+ method @WorkerThread public void cancelBugreport();
+ method @WorkerThread public void startConnectivityBugreport(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
}
public abstract static class BugreportManager.BugreportCallback {
@@ -35035,6 +35118,7 @@
field public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS";
field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
+ field public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = "android.settings.REQUEST_SCHEDULE_EXACT_ALARM";
field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE";
field public static final String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
field public static final String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS";
@@ -41151,6 +41235,7 @@
field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e
field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f
field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41
+ field public static final int ALL_MATCHING_RULES_FAILED = 2254; // 0x8ce
field public static final int APN_DISABLED = 2045; // 0x7fd
field public static final int APN_DISALLOWED_ON_ROAMING = 2059; // 0x80b
field public static final int APN_MISMATCH = 2054; // 0x806
@@ -41300,6 +41385,7 @@
field public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 2117; // 0x845
field public static final int LTE_THROTTLING_NOT_REQUIRED = 2127; // 0x84f
field public static final int MAC_FAILURE = 2183; // 0x887
+ field public static final int MATCH_ALL_RULE_NOT_ALLOWED = 2253; // 0x8cd
field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d
field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876
field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f
@@ -42085,6 +42171,7 @@
method @NonNull public android.telephony.SmsManager createForSubscriptionId(int);
method public java.util.ArrayList<java.lang.String> divideMessage(String);
method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
+ method public void downloadMultimediaMessage(@NonNull android.content.Context, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
method @NonNull public android.os.Bundle getCarrierConfigValues();
method @Deprecated public static android.telephony.SmsManager getDefault();
method public static int getDefaultSmsSubscriptionId();
@@ -42096,6 +42183,7 @@
method public void injectSmsPdu(byte[], String, android.app.PendingIntent);
method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
+ method public void sendMultimediaMessage(@NonNull android.content.Context, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long);
method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String, @Nullable String);
@@ -42465,7 +42553,7 @@
method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
method @Nullable public android.telephony.SignalStrength getSignalStrength();
method public int getSimCarrierId();
method @Nullable public CharSequence getSimCarrierIdName();
@@ -46883,6 +46971,7 @@
method public int getId();
method public android.view.KeyCharacterMap getKeyCharacterMap();
method public int getKeyboardType();
+ method @NonNull public android.hardware.lights.LightsManager getLightsManager();
method public android.view.InputDevice.MotionRange getMotionRange(int);
method public android.view.InputDevice.MotionRange getMotionRange(int, int);
method public java.util.List<android.view.InputDevice.MotionRange> getMotionRanges();
@@ -51616,6 +51705,7 @@
method public int describeContents();
method public void dump(android.util.Printer, String);
method public android.content.ComponentName getComponent();
+ method public int getConfigChanges();
method public String getId();
method public int getIsDefaultResourceId();
method public String getPackageName();
@@ -53297,6 +53387,7 @@
method public int getCheckedItemPosition();
method public android.util.SparseBooleanArray getCheckedItemPositions();
method public int getChoiceMode();
+ method public int getEdgeEffectType();
method public int getListPaddingBottom();
method public int getListPaddingLeft();
method public int getListPaddingRight();
@@ -53338,6 +53429,7 @@
method public void setChoiceMode(int);
method public void setDrawSelectorOnTop(boolean);
method public void setEdgeEffectColor(@ColorInt int);
+ method public void setEdgeEffectType(int);
method public void setFastScrollAlwaysVisible(boolean);
method public void setFastScrollEnabled(boolean);
method public void setFastScrollStyle(int);
@@ -53628,11 +53720,27 @@
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet);
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int);
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int, int);
+ method @Deprecated @Nullable public android.graphics.BlendMode getDialTintBlendMode();
+ method @Deprecated @Nullable public android.content.res.ColorStateList getDialTintList();
+ method @Deprecated @Nullable public android.graphics.BlendMode getHourHandTintBlendMode();
+ method @Deprecated @Nullable public android.content.res.ColorStateList getHourHandTintList();
+ method @Deprecated @Nullable public android.graphics.BlendMode getMinuteHandTintBlendMode();
+ method @Deprecated @Nullable public android.content.res.ColorStateList getMinuteHandTintList();
+ method @Deprecated @Nullable public android.graphics.BlendMode getSecondHandTintBlendMode();
+ method @Deprecated @Nullable public android.content.res.ColorStateList getSecondHandTintList();
method @Deprecated @Nullable public String getTimeZone();
method @Deprecated public void setDial(@NonNull android.graphics.drawable.Icon);
+ method @Deprecated public void setDialTintBlendMode(@Nullable android.graphics.BlendMode);
+ method @Deprecated public void setDialTintList(@Nullable android.content.res.ColorStateList);
method @Deprecated public void setHourHand(@NonNull android.graphics.drawable.Icon);
+ method @Deprecated public void setHourHandTintBlendMode(@Nullable android.graphics.BlendMode);
+ method @Deprecated public void setHourHandTintList(@Nullable android.content.res.ColorStateList);
method @Deprecated public void setMinuteHand(@NonNull android.graphics.drawable.Icon);
+ method @Deprecated public void setMinuteHandTintBlendMode(@Nullable android.graphics.BlendMode);
+ method @Deprecated public void setMinuteHandTintList(@Nullable android.content.res.ColorStateList);
method @Deprecated public void setSecondHand(@Nullable android.graphics.drawable.Icon);
+ method @Deprecated public void setSecondHandTintBlendMode(@Nullable android.graphics.BlendMode);
+ method @Deprecated public void setSecondHandTintList(@Nullable android.content.res.ColorStateList);
method @Deprecated public void setTimeZone(@Nullable String);
}
@@ -54330,6 +54438,7 @@
method public boolean executeKeyEvent(android.view.KeyEvent);
method public void fling(int);
method public boolean fullScroll(int);
+ method public int getEdgeEffectType();
method @ColorInt public int getLeftEdgeEffectColor();
method public int getMaxScrollAmount();
method @ColorInt public int getRightEdgeEffectColor();
@@ -54337,6 +54446,7 @@
method public boolean isSmoothScrollingEnabled();
method public boolean pageScroll(int);
method public void setEdgeEffectColor(@ColorInt int);
+ method public void setEdgeEffectType(int);
method public void setFillViewport(boolean);
method public void setLeftEdgeEffectColor(@ColorInt int);
method public void setRightEdgeEffectColor(@ColorInt int);
@@ -55055,6 +55165,9 @@
method public void setChronometer(@IdRes int, long, String, boolean);
method public void setChronometerCountDown(@IdRes int, boolean);
method public void setColor(@IdRes int, @NonNull String, @ColorRes int);
+ method public void setColorInt(@IdRes int, @NonNull String, @ColorInt int, @ColorInt int);
+ method public void setColorStateList(@IdRes int, @NonNull String, @Nullable android.content.res.ColorStateList);
+ method public void setColorStateList(@IdRes int, @NonNull String, @Nullable android.content.res.ColorStateList, @Nullable android.content.res.ColorStateList);
method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int);
method public void setCompoundButtonChecked(@IdRes int, boolean);
method public void setContentDescription(@IdRes int, CharSequence);
@@ -55065,6 +55178,7 @@
method public void setFloatDimen(@IdRes int, @NonNull String, @DimenRes int);
method public void setFloatDimen(@IdRes int, @NonNull String, float, int);
method public void setIcon(@IdRes int, String, android.graphics.drawable.Icon);
+ method public void setIcon(@IdRes int, @NonNull String, @Nullable android.graphics.drawable.Icon, @Nullable android.graphics.drawable.Icon);
method public void setImageViewBitmap(@IdRes int, android.graphics.Bitmap);
method public void setImageViewIcon(@IdRes int, android.graphics.drawable.Icon);
method public void setImageViewResource(@IdRes int, @DrawableRes int);
@@ -55095,6 +55209,12 @@
method public void setTextViewText(@IdRes int, CharSequence);
method public void setTextViewTextSize(@IdRes int, int, float);
method public void setUri(@IdRes int, String, android.net.Uri);
+ method public void setViewLayoutHeight(@IdRes int, float, int);
+ method public void setViewLayoutHeightDimen(@IdRes int, @DimenRes int);
+ method public void setViewLayoutMargin(@IdRes int, int, float, int);
+ method public void setViewLayoutMarginDimen(@IdRes int, int, @DimenRes int);
+ method public void setViewLayoutWidth(@IdRes int, float, int);
+ method public void setViewLayoutWidthDimen(@IdRes int, @DimenRes int);
method public void setViewOutlinePreferredRadius(@IdRes int, float, int);
method public void setViewOutlinePreferredRadiusDimen(@IdRes int, @DimenRes int);
method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int);
@@ -55105,6 +55225,12 @@
field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews> CREATOR;
field public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED";
field public static final String EXTRA_SHARED_ELEMENT_BOUNDS = "android.widget.extra.SHARED_ELEMENT_BOUNDS";
+ field public static final int MARGIN_BOTTOM = 3; // 0x3
+ field public static final int MARGIN_END = 5; // 0x5
+ field public static final int MARGIN_LEFT = 0; // 0x0
+ field public static final int MARGIN_RIGHT = 2; // 0x2
+ field public static final int MARGIN_START = 4; // 0x4
+ field public static final int MARGIN_TOP = 1; // 0x1
}
public static class RemoteViews.ActionException extends java.lang.RuntimeException {
@@ -55174,6 +55300,7 @@
method public void fling(int);
method public boolean fullScroll(int);
method @ColorInt public int getBottomEdgeEffectColor();
+ method public int getEdgeEffectType();
method public int getMaxScrollAmount();
method @ColorInt public int getTopEdgeEffectColor();
method public boolean isFillViewport();
@@ -55182,6 +55309,7 @@
method public void scrollToDescendant(@NonNull android.view.View);
method public void setBottomEdgeEffectColor(@ColorInt int);
method public void setEdgeEffectColor(@ColorInt int);
+ method public void setEdgeEffectType(int);
method public void setFillViewport(boolean);
method public void setSmoothScrollingEnabled(boolean);
method public void setTopEdgeEffectColor(@ColorInt int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index de02d0b..dd9582f 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -135,7 +135,7 @@
}
public final class MediaSessionManager {
- method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.os.Handler);
+ method public void addOnActiveSessionsChangedListener(@Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent);
method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean);
method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
@@ -189,6 +189,10 @@
field public static final int TRANSPORT_TEST = 7; // 0x7
}
+ public class NetworkWatchlistManager {
+ method @Nullable public byte[] getWatchlistConfigHash();
+ }
+
public final class Proxy {
method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7b7518d..17783b7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1826,7 +1826,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public android.bluetooth.BufferConstraints getBufferConstraints();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getDynamicBufferSupport();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferMillis(int, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferLengthMillis(int, int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; // 0x1
field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; // 0x2
@@ -1942,6 +1942,10 @@
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
}
+ public final class BluetoothMapClient implements android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(android.Manifest.permission.SEND_SMS) public boolean sendMessage(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.net.Uri>, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent);
+ }
+
public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
@@ -1974,6 +1978,7 @@
field public static final int CONNECTION_POLICY_FORBIDDEN = 0; // 0x0
field public static final int CONNECTION_POLICY_UNKNOWN = -1; // 0xffffffff
field public static final int HEADSET_CLIENT = 16; // 0x10
+ field public static final int MAP_CLIENT = 18; // 0x12
field public static final int PAN = 5; // 0x5
field public static final int PBAP_CLIENT = 17; // 0x11
field @Deprecated public static final int PRIORITY_OFF = 0; // 0x0
@@ -2027,7 +2032,7 @@
public final class BufferConstraints implements android.os.Parcelable {
ctor public BufferConstraints(@NonNull java.util.List<android.bluetooth.BufferConstraint>);
method public int describeContents();
- method @Nullable public android.bluetooth.BufferConstraint getCodec(int);
+ method @Nullable public android.bluetooth.BufferConstraint forCodec(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int BUFFER_CODEC_MAX_NUM = 32; // 0x20
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR;
@@ -3398,42 +3403,12 @@
package android.hardware.lights {
- public final class Light implements android.os.Parcelable {
- method public int describeContents();
- method public int getId();
- method public int getOrdinal();
- method public int getType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
- }
-
public final class LightState implements android.os.Parcelable {
- ctor public LightState(@ColorInt int);
- method public int describeContents();
- method @ColorInt public int getColor();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR;
+ ctor @Deprecated public LightState(@ColorInt int);
}
- public final class LightsManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights();
- method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession();
- field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
- }
-
- public final class LightsManager.LightsSession implements java.lang.AutoCloseable {
- method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close();
- method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest);
- }
-
- public final class LightsRequest {
- }
-
- public static final class LightsRequest.Builder {
- ctor public LightsRequest.Builder();
- method @NonNull public android.hardware.lights.LightsRequest build();
- method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light);
- method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+ public abstract class LightsManager {
+ field @Deprecated public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
}
}
@@ -4713,6 +4688,7 @@
}
public class LocationManager {
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener);
method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @Nullable public String getExtraLocationControllerPackage();
@@ -4727,7 +4703,7 @@
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
- method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerProviderRequestListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.Listener);
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void removeProviderRequestChangedListener(@NonNull android.location.provider.ProviderRequest.ChangedListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -4736,7 +4712,6 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback);
- method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void unregisterProviderRequestListener(@NonNull android.location.provider.ProviderRequest.Listener);
}
public final class LocationRequest implements android.os.Parcelable {
@@ -4886,7 +4861,7 @@
method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource);
}
- public static interface ProviderRequest.Listener {
+ public static interface ProviderRequest.ChangedListener {
method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest);
}
@@ -5097,7 +5072,7 @@
}
public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
- method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener);
}
public static interface MediaPlayer.OnRtpRxNoticeListener {
@@ -5704,7 +5679,7 @@
method public void updateResourcePriority(int, int);
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
- field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL
+ field public static final long INVALID_FILTER_ID_LONG = -1L; // 0xffffffffffffffffL
field public static final int INVALID_FIRST_MACROBLOCK_IN_SLICE = -1; // 0xffffffff
field public static final int INVALID_FRONTEND_ID = -1; // 0xffffffff
field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff
@@ -5885,7 +5860,7 @@
method public int getItemFragmentIndex();
method public int getItemId();
method public int getLastItemFragmentIndex();
- method public int getMpuSequenceNumber();
+ method @IntRange(from=0) public int getMpuSequenceNumber();
}
public class DownloadSettings extends android.media.tv.tuner.filter.Settings {
@@ -5903,7 +5878,7 @@
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
method public int flush();
method public int getId();
- method public long getId64Bit();
+ method public long getIdLong();
method public int read(@NonNull byte[], long, long);
method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
method public int setMonitorEventMask(int);
@@ -5994,7 +5969,7 @@
method public long getDataLength();
method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData();
method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
- method public int getMpuSequenceNumber();
+ method @IntRange(from=0) public int getMpuSequenceNumber();
method public long getOffset();
method public long getPts();
method public int getStreamId();
@@ -6019,7 +5994,7 @@
public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getDataLength();
method public int getFirstMacroblockInSlice();
- method public int getMpuSequenceNumber();
+ method @IntRange(from=0) public int getMpuSequenceNumber();
method public long getPts();
method public int getScHevcIndexMask();
method public int getTsIndexMask();
@@ -6027,7 +6002,7 @@
public class PesEvent extends android.media.tv.tuner.filter.FilterEvent {
method public int getDataLength();
- method public int getMpuSequenceNumber();
+ method @IntRange(from=0) public int getMpuSequenceNumber();
method public int getStreamId();
}
@@ -7941,6 +7916,28 @@
}
+package android.net.vcn {
+
+ public class VcnManager {
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties);
+ method public void removeVcnNetworkPolicyListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+ }
+
+ public static interface VcnManager.VcnNetworkPolicyListener {
+ method public void onPolicyChanged();
+ }
+
+ public final class VcnNetworkPolicyResult implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+ method public boolean isTeardownRequested();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnNetworkPolicyResult> CREATOR;
+ }
+
+}
+
package android.net.wifi {
public final class WifiMigration {
@@ -8288,7 +8285,7 @@
public final class BugreportManager {
method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence);
- method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
+ method @RequiresPermission(android.Manifest.permission.DUMP) @WorkerThread public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
}
public final class BugreportParams {
@@ -8580,7 +8577,7 @@
method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
method @NonNull public android.os.BatterySaverPolicyConfig getFullPowerSavePolicy();
- method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger();
+ method public int getPowerSaveModeTrigger();
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplayAvailable();
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressed();
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String);
@@ -9221,6 +9218,7 @@
field public static final String NAMESPACE_PERMISSIONS = "permissions";
field public static final String NAMESPACE_PRIVACY = "privacy";
field public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot";
+ field public static final String NAMESPACE_REBOOT_READINESS = "reboot_readiness";
field public static final String NAMESPACE_ROLLBACK = "rollback";
field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
field public static final String NAMESPACE_RUNTIME = "runtime";
@@ -10765,7 +10763,7 @@
method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method @Nullable public final String getTelecomCallId();
method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
- method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
+ method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(@NonNull android.telecom.Connection.CallFilteringCompletionInfo);
method public final void resetConnectionTime();
method public void setCallDirection(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
@@ -10781,6 +10779,16 @@
field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800
}
+ public static final class Connection.CallFilteringCompletionInfo implements android.os.Parcelable {
+ ctor public Connection.CallFilteringCompletionInfo(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, @Nullable android.content.ComponentName);
+ method public int describeContents();
+ method @Nullable public android.telecom.CallScreeningService.CallResponse getCallResponse();
+ method @Nullable public android.content.ComponentName getCallScreeningComponent();
+ method public boolean isBlocked();
+ method public boolean isInContacts();
+ field @NonNull public static final android.os.Parcelable.Creator<android.telecom.Connection.CallFilteringCompletionInfo> CREATOR;
+ }
+
public final class ConnectionRequest implements android.os.Parcelable {
method @Nullable public String getTelecomCallId();
}
@@ -10942,7 +10950,7 @@
}
public final class RemoteConnection {
- method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
+ method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(@NonNull android.telecom.Connection.CallFilteringCompletionInfo);
method @Deprecated public void setAudioState(android.telecom.AudioState);
}
@@ -12253,6 +12261,7 @@
method public long getRetryDurationMillis();
method @Nullable public android.telephony.data.SliceInfo getSliceInfo();
method @Deprecated public int getSuggestedRetryTime();
+ method @NonNull public java.util.List<android.telephony.data.TrafficDescriptor> getTrafficDescriptors();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1
@@ -12288,6 +12297,7 @@
method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long);
method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo);
method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setTrafficDescriptors(@NonNull java.util.List<android.telephony.data.TrafficDescriptor>);
}
public final class DataProfile implements android.os.Parcelable {
@@ -12358,7 +12368,7 @@
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
- method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @NonNull android.telephony.data.DataServiceCallback);
+ method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @Nullable android.telephony.data.TrafficDescriptor, boolean, @NonNull android.telephony.data.DataServiceCallback);
}
public class DataServiceCallback {
@@ -12457,6 +12467,15 @@
method @NonNull public android.telephony.data.ThrottleStatus.Builder setTransportType(int);
}
+ public final class TrafficDescriptor implements android.os.Parcelable {
+ ctor public TrafficDescriptor(@Nullable String, @Nullable String);
+ method public int describeContents();
+ method @Nullable public String getDnn();
+ method @Nullable public String getOsAppId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR;
+ }
+
}
package android.telephony.euicc {
@@ -12904,6 +12923,7 @@
field public static final String EXTRA_EMERGENCY_CALL = "e_call";
field public static final String EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED = "android.telephony.ims.extra.EXTENDING_TO_CONFERENCE_SUPPORTED";
field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER";
+ field public static final String EXTRA_IS_BUSINESS_CALL = "android.telephony.ims.extra.IS_BUSINESS_CALL";
field public static final String EXTRA_IS_CALL_PULL = "CallPull";
field public static final String EXTRA_IS_CROSS_SIM_CALL = "android.telephony.ims.extra.IS_CROSS_SIM_CALL";
field public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION";
@@ -14275,21 +14295,10 @@
public final class UwbManager {
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getAngleOfArrivalSupport();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerInitiatorSession();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerResponderSession();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxSimultaneousSessions();
method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
- method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.List<java.lang.Integer> getSupportedChannelNumbers();
- method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.Set<java.lang.Integer> getSupportedPreambleCodeIndices();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public boolean isRangingSupported();
method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
- field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; // 0x2
- field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; // 0x3
- field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; // 0x4
- field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1; // 0x1
}
public static interface UwbManager.AdapterStateCallback {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e0e8453..f0a2a49 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -100,7 +100,7 @@
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
field public static final long DROP_CLOSE_SYSTEM_DIALOGS = 174664120L; // 0xa6929b8L
field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL
- field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7
+ field public static final int PROCESS_CAPABILITY_ALL = 15; // 0xf
field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2
@@ -580,6 +580,14 @@
}
+package android.appwidget {
+
+ public class AppWidgetManager {
+ method public void setBindAppWidgetPermission(@NonNull String, int, boolean);
+ }
+
+}
+
package android.bluetooth {
public final class BluetoothClass implements android.os.Parcelable {
@@ -1001,14 +1009,6 @@
}
-package android.hardware.lights {
-
- public final class LightsManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light);
- }
-
-}
-
package android.hardware.soundtrigger {
public class KeyphraseEnrollmentInfo {
@@ -1640,6 +1640,7 @@
public class StorageManager {
method @NonNull public static java.util.UUID convert(@NonNull String);
method @NonNull public static String convert(@NonNull java.util.UUID);
+ method public static boolean isUserKeyUnlocked(int);
}
public final class StorageVolume implements android.os.Parcelable {
@@ -2555,6 +2556,10 @@
method @NonNull public static android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(@NonNull java.util.List<android.view.inputmethod.InlineSuggestion>);
}
+ public final class InputMethodInfo implements android.os.Parcelable {
+ ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
+ }
+
public final class InputMethodManager {
method public int getDisplayId();
method public boolean hasActiveInputConnection(@Nullable android.view.View);
diff --git a/core/java/android/app/ActivityManager.aidl b/core/java/android/app/ActivityManager.aidl
index 341393c..eb7cac07 100644
--- a/core/java/android/app/ActivityManager.aidl
+++ b/core/java/android/app/ActivityManager.aidl
@@ -17,6 +17,7 @@
package android.app;
parcelable ActivityManager.MemoryInfo;
+parcelable ActivityManager.PendingIntentInfo;
parcelable ActivityManager.ProcessErrorStateInfo;
parcelable ActivityManager.RecentTaskInfo;
parcelable ActivityManager.TaskDescription;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e2426d1..07e70dc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -616,11 +616,15 @@
@TestApi
public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 2;
+ /** @hide Process can access network despite any power saving resrictions */
+ public static final int PROCESS_CAPABILITY_NETWORK = 1 << 3;
+
/** @hide all capabilities, the ORing of all flags in {@link ProcessCapability}*/
@TestApi
public static final int PROCESS_CAPABILITY_ALL = PROCESS_CAPABILITY_FOREGROUND_LOCATION
| PROCESS_CAPABILITY_FOREGROUND_CAMERA
- | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
+ | PROCESS_CAPABILITY_NETWORK;
/**
* All explicit capabilities. These are capabilities that need to be specified from manifest
* file.
@@ -646,6 +650,15 @@
pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-');
pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-');
pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-');
+ pw.print((caps & PROCESS_CAPABILITY_NETWORK) != 0 ? 'N' : '-');
+ }
+
+ /** @hide */
+ public static void printCapabilitiesSummary(StringBuilder sb, @ProcessCapability int caps) {
+ sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-');
+ sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-');
+ sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-');
+ sb.append((caps & PROCESS_CAPABILITY_NETWORK) != 0 ? 'N' : '-');
}
/**
@@ -656,13 +669,21 @@
printCapabilitiesSummary(pw, caps);
final int remain = caps & ~(PROCESS_CAPABILITY_FOREGROUND_LOCATION
| PROCESS_CAPABILITY_FOREGROUND_CAMERA
- | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE);
+ | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
+ | PROCESS_CAPABILITY_NETWORK);
if (remain != 0) {
pw.print('+');
pw.print(remain);
}
}
+ /** @hide */
+ public static String getCapabilitiesSummary(@ProcessCapability int caps) {
+ final StringBuilder sb = new StringBuilder();
+ printCapabilitiesSummary(sb, caps);
+ return sb.toString();
+ }
+
// NOTE: If PROCESS_STATEs are added, then new fields must be added
// to frameworks/base/core/proto/android/app/enums.proto and the following method must
// be updated to correctly map between them.
@@ -4485,6 +4506,80 @@
}
}
+ /** @hide */
+ public static String procStateToString(int procState) {
+ final String procStateStr;
+ switch (procState) {
+ case ActivityManager.PROCESS_STATE_PERSISTENT:
+ procStateStr = "PER ";
+ break;
+ case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
+ procStateStr = "PERU";
+ break;
+ case ActivityManager.PROCESS_STATE_TOP:
+ procStateStr = "TOP ";
+ break;
+ case ActivityManager.PROCESS_STATE_BOUND_TOP:
+ procStateStr = "BTOP";
+ break;
+ case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
+ procStateStr = "FGS ";
+ break;
+ case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
+ procStateStr = "BFGS";
+ break;
+ case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+ procStateStr = "IMPF";
+ break;
+ case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+ procStateStr = "IMPB";
+ break;
+ case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
+ procStateStr = "TRNB";
+ break;
+ case ActivityManager.PROCESS_STATE_BACKUP:
+ procStateStr = "BKUP";
+ break;
+ case ActivityManager.PROCESS_STATE_SERVICE:
+ procStateStr = "SVC ";
+ break;
+ case ActivityManager.PROCESS_STATE_RECEIVER:
+ procStateStr = "RCVR";
+ break;
+ case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
+ procStateStr = "TPSL";
+ break;
+ case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
+ procStateStr = "HVY ";
+ break;
+ case ActivityManager.PROCESS_STATE_HOME:
+ procStateStr = "HOME";
+ break;
+ case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
+ procStateStr = "LAST";
+ break;
+ case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+ procStateStr = "CAC ";
+ break;
+ case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+ procStateStr = "CACC";
+ break;
+ case ActivityManager.PROCESS_STATE_CACHED_RECENT:
+ procStateStr = "CRE ";
+ break;
+ case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
+ procStateStr = "CEM ";
+ break;
+ case ActivityManager.PROCESS_STATE_NONEXISTENT:
+ procStateStr = "NONE";
+ break;
+ default:
+ procStateStr = "??";
+ break;
+ }
+ return procStateStr;
+ }
+
/**
* The AppTask allows you to manage your own application's tasks.
* See {@link android.app.ActivityManager#getAppTasks()}
@@ -4681,4 +4776,71 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * A subset of immutable pending intent information suitable for caching on the client side.
+ *
+ * @hide
+ */
+ public static final class PendingIntentInfo implements Parcelable {
+
+ private final String mCreatorPackage;
+ private final int mCreatorUid;
+ private final boolean mImmutable;
+ private final int mIntentSenderType;
+
+ public PendingIntentInfo(String creatorPackage, int creatorUid, boolean immutable,
+ int intentSenderType) {
+ mCreatorPackage = creatorPackage;
+ mCreatorUid = creatorUid;
+ mImmutable = immutable;
+ mIntentSenderType = intentSenderType;
+ }
+
+ public String getCreatorPackage() {
+ return mCreatorPackage;
+ }
+
+ public int getCreatorUid() {
+ return mCreatorUid;
+ }
+
+ public boolean isImmutable() {
+ return mImmutable;
+ }
+
+ public int getIntentSenderType() {
+ return mIntentSenderType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeString(mCreatorPackage);
+ parcel.writeInt(mCreatorUid);
+ parcel.writeBoolean(mImmutable);
+ parcel.writeInt(mIntentSenderType);
+ }
+
+ public static final @NonNull Creator<PendingIntentInfo> CREATOR =
+ new Creator<PendingIntentInfo>() {
+ @Override
+ public PendingIntentInfo createFromParcel(Parcel in) {
+ return new PendingIntentInfo(
+ /* creatorPackage= */ in.readString(),
+ /* creatorUid= */ in.readInt(),
+ /* immutable= */ in.readBoolean(),
+ /* intentSenderType= */ in.readInt());
+ }
+
+ @Override
+ public PendingIntentInfo[] newArray(int size) {
+ return new PendingIntentInfo[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3d9f612..47d2e7c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5141,7 +5141,7 @@
private void onCoreSettingsChange() {
if (updateDebugViewAttributeState()) {
// request all activities to relaunch for the changes to take place
- relaunchAllActivities(false /* preserveWindows */);
+ relaunchAllActivities(false /* preserveWindows */, "onCoreSettingsChange");
}
}
@@ -5160,7 +5160,8 @@
return previousState != View.sDebugViewAttributes;
}
- private void relaunchAllActivities(boolean preserveWindows) {
+ private void relaunchAllActivities(boolean preserveWindows, String reason) {
+ Log.i(TAG, "Relaunch all activities: " + reason);
for (int i = mActivities.size() - 1; i >= 0; i--) {
scheduleRelaunchActivityIfPossible(mActivities.valueAt(i), preserveWindows);
}
@@ -5535,6 +5536,7 @@
void scheduleRelaunchActivity(IBinder token) {
final ActivityClientRecord r = mActivities.get(token);
if (r != null) {
+ Log.i(TAG, "Schedule relaunch activity: " + r.activityInfo.name);
scheduleRelaunchActivityIfPossible(r, !r.stopped /* preserveWindow */);
}
}
@@ -6079,7 +6081,7 @@
handleConfigurationChanged(newConfig, null);
// Preserve windows to avoid black flickers when overlays change.
- relaunchAllActivities(true /* preserveWindows */);
+ relaunchAllActivities(true /* preserveWindows */, "handleApplicationInfoChanged");
}
static void freeTextLayoutCachesIfNeeded(int configDiff) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1906ee4..2f3b50b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2551,9 +2551,9 @@
false, // READ_MEDIA_AUDIO
false, // WRITE_MEDIA_AUDIO
false, // READ_MEDIA_VIDEO
- false, // WRITE_MEDIA_VIDEO
+ true, // WRITE_MEDIA_VIDEO
false, // READ_MEDIA_IMAGES
- false, // WRITE_MEDIA_IMAGES
+ true, // WRITE_MEDIA_IMAGES
true, // LEGACY_STORAGE
false, // ACCESS_ACCESSIBILITY
false, // READ_DEVICE_IDENTIFIERS
@@ -6553,14 +6553,15 @@
public interface OnOpNotedListener {
/**
* Called when an op was noted.
- *
* @param code The op code.
* @param uid The UID performing the operation.
* @param packageName The package performing the operation.
+ * @param attributionTag The attribution tag performing the operation.
* @param flags The flags of this op
* @param result The result of the note.
*/
- void onOpNoted(int code, int uid, String packageName, @OpFlags int flags, @Mode int result);
+ void onOpNoted(int code, int uid, String packageName, String attributionTag,
+ @OpFlags int flags, @Mode int result);
}
/**
@@ -6593,14 +6594,15 @@
* Called when an op was started.
*
* Note: This is only for op starts. It is not called when an op is noted or stopped.
- *
* @param op The op code.
* @param uid The UID performing the operation.
* @param packageName The package performing the operation.
+ * @param attributionTag The attribution tag performing the operation.
* @param flags The flags of this op
* @param result The result of the start.
*/
- void onOpStarted(int op, int uid, String packageName, @OpFlags int flags, @Mode int result);
+ void onOpStarted(int op, int uid, String packageName, String attributionTag,
+ @OpFlags int flags, @Mode int result);
}
AppOpsManager(Context context, IAppOpsService service) {
@@ -7183,8 +7185,9 @@
}
cb = new IAppOpsStartedCallback.Stub() {
@Override
- public void opStarted(int op, int uid, String packageName, int flags, int mode) {
- callback.onOpStarted(op, uid, packageName, flags, mode);
+ public void opStarted(int op, int uid, String packageName, String attributionTag,
+ int flags, int mode) {
+ callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode);
}
};
mStartedWatchers.put(callback, cb);
@@ -7250,8 +7253,9 @@
}
cb = new IAppOpsNotedCallback.Stub() {
@Override
- public void opNoted(int op, int uid, String packageName, int flags, int mode) {
- callback.onOpNoted(op, uid, packageName, flags, mode);
+ public void opNoted(int op, int uid, String packageName, String attributionTag,
+ int flags, int mode) {
+ callback.onOpNoted(op, uid, packageName, attributionTag, flags, mode);
}
};
mNotedWatchers.put(callback, cb);
diff --git a/core/java/android/app/BackgroundServiceStartNotAllowedException.java b/core/java/android/app/BackgroundServiceStartNotAllowedException.java
new file mode 100644
index 0000000..f6361b5
--- /dev/null
+++ b/core/java/android/app/BackgroundServiceStartNotAllowedException.java
@@ -0,0 +1,62 @@
+/*
+ * 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.app;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Exception thrown when an app tries to start a background {@link Service} when it's not allowed to
+ * do so.
+ */
+public final class BackgroundServiceStartNotAllowedException
+ extends ServiceStartNotAllowedException implements Parcelable {
+ /**
+ * Constructor.
+ */
+ public BackgroundServiceStartNotAllowedException(@NonNull String message) {
+ super(message);
+ }
+
+ BackgroundServiceStartNotAllowedException(@NonNull Parcel source) {
+ super(source.readString());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(getMessage());
+ }
+
+ public static final @NonNull Creator<android.app.BackgroundServiceStartNotAllowedException>
+ CREATOR = new Creator<android.app.BackgroundServiceStartNotAllowedException>() {
+ @NonNull
+ public android.app.BackgroundServiceStartNotAllowedException createFromParcel(
+ Parcel source) {
+ return new android.app.BackgroundServiceStartNotAllowedException(source);
+ }
+
+ @NonNull
+ public android.app.BackgroundServiceStartNotAllowedException[] newArray(int size) {
+ return new android.app.BackgroundServiceStartNotAllowedException[size];
+ }
+ };
+}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 85fb543..bc79813 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1796,7 +1796,7 @@
"Unable to start service " + service
+ ": " + cn.getClassName());
} else if (cn.getPackageName().equals("?")) {
- throw new IllegalStateException(
+ throw ServiceStartNotAllowedException.newInstance(requireForeground,
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
diff --git a/core/java/android/app/ForegroundServiceStartNotAllowedException.java b/core/java/android/app/ForegroundServiceStartNotAllowedException.java
new file mode 100644
index 0000000..41eeada2
--- /dev/null
+++ b/core/java/android/app/ForegroundServiceStartNotAllowedException.java
@@ -0,0 +1,62 @@
+/*
+ * 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.app;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Exception thrown when an app tries to start a foreground {@link Service} when it's not allowed to
+ * do so.
+ */
+public final class ForegroundServiceStartNotAllowedException
+ extends ServiceStartNotAllowedException implements Parcelable {
+ /**
+ * Constructor.
+ */
+ public ForegroundServiceStartNotAllowedException(@NonNull String message) {
+ super(message);
+ }
+
+ ForegroundServiceStartNotAllowedException(@NonNull Parcel source) {
+ super(source.readString());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(getMessage());
+ }
+
+ public static final @NonNull Creator<android.app.ForegroundServiceStartNotAllowedException>
+ CREATOR = new Creator<android.app.ForegroundServiceStartNotAllowedException>() {
+ @NonNull
+ public android.app.ForegroundServiceStartNotAllowedException createFromParcel(
+ Parcel source) {
+ return new android.app.ForegroundServiceStartNotAllowedException(source);
+ }
+
+ @NonNull
+ public android.app.ForegroundServiceStartNotAllowedException[] newArray(int size) {
+ return new android.app.ForegroundServiceStartNotAllowedException[size];
+ }
+ };
+}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 4ad13e1..0bc5958 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -17,6 +17,7 @@
package android.app;
import android.app.ActivityManager;
+import android.app.ActivityManager.PendingIntentInfo;
import android.app.ActivityTaskManager;
import android.app.ApplicationErrorReport;
import android.app.ApplicationExitInfo;
@@ -248,7 +249,7 @@
in IBinder token, in String resultWho, int requestCode, in Intent[] intents,
in String[] resolvedTypes, int flags, in Bundle options, int userId);
void cancelIntentSender(in IIntentSender sender);
- String getPackageForIntentSender(in IIntentSender sender);
+ ActivityManager.PendingIntentInfo getInfoForIntentSender(in IIntentSender sender);
void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
void unregisterIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
void enterSafeMode();
@@ -293,7 +294,6 @@
int operationType);
void backupAgentCreated(in String packageName, in IBinder agent, int userId);
void unbindBackupAgent(in ApplicationInfo appInfo);
- int getUidForIntentSender(in IIntentSender sender);
int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
boolean requireFull, in String name, in String callerPackage);
void addPackageDependency(in String packageName);
@@ -345,7 +345,6 @@
@UnsupportedAppUsage
void unregisterProcessObserver(in IProcessObserver observer);
boolean isIntentSenderTargetedToPackage(in IIntentSender sender);
- boolean isIntentSenderImmutable(in IIntentSender sender);
@UnsupportedAppUsage
void updatePersistentConfiguration(in Configuration values);
void updatePersistentConfigurationWithAttribution(in Configuration values,
@@ -375,8 +374,6 @@
void unstableProviderDied(in IBinder connection);
@UnsupportedAppUsage
boolean isIntentSenderAnActivity(in IIntentSender sender);
- boolean isIntentSenderAForegroundService(in IIntentSender sender);
- boolean isIntentSenderABroadcast(in IIntentSender sender);
/** @deprecated Use {@link startActivityAsUserWithFeature} instead */
@UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@code android.content.Context#createContextAsUser(android.os.UserHandle, int)} and {@link android.content.Context#startActivity(android.content.Intent)} instead")
int startActivityAsUser(in IApplicationThread caller, in String callingPackage,
@@ -710,6 +707,4 @@
/** Called by PendingIntent.queryIntentComponents() */
List<ResolveInfo> queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags);
-
- boolean isIntentSenderAService(in IIntentSender sender);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 899cdb5..f7304fb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1371,6 +1371,18 @@
public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
/**
+ * {@link #extras} key: the color used as a hint for the Answer action button of a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}.
+ */
+ public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
+
+ /**
+ * {@link #extras} key: the color used as a hint for the Decline or Hang Up action button of a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}.
+ */
+ public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
+
+ /**
* {@link #extras} key: whether the notification should be colorized as
* supplied to {@link Builder#setColorized(boolean)}.
*/
@@ -5444,6 +5456,11 @@
return p.allowColorization && mN.isColorized();
}
+ private boolean isCallActionColorCustomizable(StandardTemplateParams p) {
+ return isColorized(p) && mContext.getResources().getBoolean(
+ R.bool.config_callNotificationActionColorsRequireColorized);
+ }
+
private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
if (mN.mSmallIcon == null && mN.icon != 0) {
mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
@@ -9066,6 +9083,8 @@
private PendingIntent mAnswerIntent;
private PendingIntent mDeclineIntent;
private PendingIntent mHangUpIntent;
+ private Integer mAnswerButtonColor;
+ private Integer mDeclineButtonColor;
private Icon mVerificationIcon;
private CharSequence mVerificationText;
@@ -9176,6 +9195,28 @@
}
/**
+ * Optional color to be used as a hint for the Answer action button's color.
+ * The system may change this color to ensure sufficient contrast with the background.
+ * The system may choose to disregard this hint if the notification is not colorized.
+ */
+ @NonNull
+ public CallStyle setAnswerButtonColorHint(@ColorInt int color) {
+ mAnswerButtonColor = color;
+ return this;
+ }
+
+ /**
+ * Optional color to be used as a hint for the Decline or Hang Up action button's color.
+ * The system may change this color to ensure sufficient contrast with the background.
+ * The system may choose to disregard this hint if the notification is not colorized.
+ */
+ @NonNull
+ public CallStyle setDeclineButtonColorHint(@ColorInt int color) {
+ mDeclineButtonColor = color;
+ return this;
+ }
+
+ /**
* @hide
*/
public boolean displayCustomViewInline() {
@@ -9234,40 +9275,47 @@
}
@NonNull
- private Action makeNegativeAction() {
+ private Action makeNegativeAction(@NonNull StandardTemplateParams p) {
if (mDeclineIntent == null) {
- return makeAction(R.drawable.ic_call_decline,
+ return makeAction(p, R.drawable.ic_call_decline,
R.string.call_notification_hang_up_action,
- R.color.call_notification_decline_color, mHangUpIntent);
+ mDeclineButtonColor, R.color.call_notification_decline_color,
+ mHangUpIntent);
} else {
- return makeAction(R.drawable.ic_call_decline,
+ return makeAction(p, R.drawable.ic_call_decline,
R.string.call_notification_decline_action,
- R.color.call_notification_decline_color, mDeclineIntent);
+ mDeclineButtonColor, R.color.call_notification_decline_color,
+ mDeclineIntent);
}
}
@Nullable
- private Action makeAnswerAction() {
- return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer,
+ private Action makeAnswerAction(@NonNull StandardTemplateParams p) {
+ return mAnswerIntent == null ? null : makeAction(p, R.drawable.ic_call_answer,
R.string.call_notification_answer_action,
- R.color.call_notification_answer_color, mAnswerIntent);
+ mAnswerButtonColor, R.color.call_notification_answer_color,
+ mAnswerIntent);
}
@NonNull
- private Action makeAction(@DrawableRes int icon, @StringRes int title,
- @ColorRes int colorRes, PendingIntent intent) {
+ private Action makeAction(@NonNull StandardTemplateParams p,
+ @DrawableRes int icon, @StringRes int title,
+ @ColorInt Integer colorInt, @ColorRes int defaultColorRes, PendingIntent intent) {
+ if (colorInt == null || !mBuilder.isCallActionColorCustomizable(p)) {
+ colorInt = mBuilder.mContext.getColor(defaultColorRes);
+ }
Action action = new Action.Builder(Icon.createWithResource("", icon),
new SpannableStringBuilder().append(mBuilder.mContext.getString(title),
- new ForegroundColorSpan(mBuilder.mContext.getColor(colorRes)),
+ new ForegroundColorSpan(colorInt),
SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE),
intent).build();
action.getExtras().putBoolean(KEY_ACTION_PRIORITY, true);
return action;
}
- private ArrayList<Action> makeActionsList() {
- final Action negativeAction = makeNegativeAction();
- final Action answerAction = makeAnswerAction();
+ private ArrayList<Action> makeActionsList(@NonNull StandardTemplateParams p) {
+ final Action negativeAction = makeNegativeAction(p);
+ final Action answerAction = makeAnswerAction(p);
ArrayList<Action> actions = new ArrayList<>(MAX_ACTION_BUTTONS);
final Action lastAction;
@@ -9356,7 +9404,7 @@
// Create the buttons for the generated actions list.
int i = 0;
- for (Action action : makeActionsList()) {
+ for (Action action : makeActionsList(p)) {
final RemoteViews button = mBuilder.generateActionButton(action, emphasizedMode, p);
if (i > 0) {
// Clear start margin from non-first buttons to reduce the gap between buttons.
@@ -9421,6 +9469,12 @@
if (mHangUpIntent != null) {
extras.putParcelable(EXTRA_HANG_UP_INTENT, mHangUpIntent);
}
+ if (mAnswerButtonColor != null) {
+ extras.putInt(EXTRA_ANSWER_COLOR, mAnswerButtonColor);
+ }
+ if (mDeclineButtonColor != null) {
+ extras.putInt(EXTRA_DECLINE_COLOR, mDeclineButtonColor);
+ }
fixTitleAndTextExtras(extras);
}
@@ -9447,6 +9501,10 @@
mAnswerIntent = extras.getParcelable(EXTRA_ANSWER_INTENT);
mDeclineIntent = extras.getParcelable(EXTRA_DECLINE_INTENT);
mHangUpIntent = extras.getParcelable(EXTRA_HANG_UP_INTENT);
+ mAnswerButtonColor = extras.containsKey(EXTRA_ANSWER_COLOR)
+ ? extras.getInt(EXTRA_ANSWER_COLOR) : null;
+ mDeclineButtonColor = extras.containsKey(EXTRA_DECLINE_COLOR)
+ ? extras.getInt(EXTRA_DECLINE_COLOR) : null;
}
/**
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 671315f..2f06bdc 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -16,6 +16,11 @@
package android.app;
+import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
+import static android.app.ActivityManager.INTENT_SENDER_BROADCAST;
+import static android.app.ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.INTENT_SENDER_SERVICE;
+
import android.Manifest.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -25,6 +30,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
import android.annotation.TestApi;
+import android.app.ActivityManager.PendingIntentInfo;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -122,6 +128,9 @@
private IBinder mWhitelistToken;
private ArraySet<CancelListener> mCancelListeners;
+ // cached pending intent information
+ private @Nullable PendingIntentInfo mCachedInfo;
+
/**
* It is now required to specify either {@link #FLAG_IMMUTABLE}
* or {@link #FLAG_MUTABLE} when creating a PendingIntent.
@@ -463,7 +472,7 @@
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ INTENT_SENDER_ACTIVITY, packageName,
context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, options, user.getIdentifier());
@@ -596,7 +605,7 @@
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ INTENT_SENDER_ACTIVITY, packageName,
context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes,
flags, options, user.getIdentifier());
return target != null ? new PendingIntent(target) : null;
@@ -650,7 +659,7 @@
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_BROADCAST, packageName,
+ INTENT_SENDER_BROADCAST, packageName,
context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, userHandle.getIdentifier());
@@ -687,7 +696,7 @@
public static PendingIntent getService(Context context, int requestCode,
@NonNull Intent intent, @Flags int flags) {
return buildServicePendingIntent(context, requestCode, intent, flags,
- ActivityManager.INTENT_SENDER_SERVICE);
+ INTENT_SENDER_SERVICE);
}
/**
@@ -717,7 +726,7 @@
public static PendingIntent getForegroundService(Context context, int requestCode,
@NonNull Intent intent, @Flags int flags) {
return buildServicePendingIntent(context, requestCode, intent, flags,
- ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE);
+ INTENT_SENDER_FOREGROUND_SERVICE);
}
private static PendingIntent buildServicePendingIntent(Context context, int requestCode,
@@ -1001,12 +1010,7 @@
*/
@Deprecated
public String getTargetPackage() {
- try {
- return ActivityManager.getService()
- .getPackageForIntentSender(mTarget);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getCreatorPackage();
}
/**
@@ -1029,12 +1033,7 @@
*/
@Nullable
public String getCreatorPackage() {
- try {
- return ActivityManager.getService()
- .getPackageForIntentSender(mTarget);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getCachedInfo().getCreatorPackage();
}
/**
@@ -1056,12 +1055,7 @@
* none associated with it.
*/
public int getCreatorUid() {
- try {
- return ActivityManager.getService()
- .getUidForIntentSender(mTarget);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getCachedInfo().getCreatorUid();
}
/**
@@ -1154,13 +1148,8 @@
*/
@Nullable
public UserHandle getCreatorUserHandle() {
- try {
- int uid = ActivityManager.getService()
- .getUidForIntentSender(mTarget);
- return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ int uid = getCachedInfo().getCreatorUid();
+ return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
}
/**
@@ -1180,12 +1169,7 @@
* Check if this PendingIntent is marked with {@link #FLAG_IMMUTABLE}.
*/
public boolean isImmutable() {
- try {
- return ActivityManager.getService()
- .isIntentSenderImmutable(mTarget);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getCachedInfo().isImmutable();
}
/**
@@ -1193,48 +1177,28 @@
* {@link #getActivity} or {@link #getActivities}.
*/
public boolean isActivity() {
- try {
- return ActivityManager.getService()
- .isIntentSenderAnActivity(mTarget);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getCachedInfo().getIntentSenderType() == INTENT_SENDER_ACTIVITY;
}
/**
* @return TRUE if this {@link PendingIntent} was created with {@link #getForegroundService}.
*/
public boolean isForegroundService() {
- try {
- return ActivityManager.getService()
- .isIntentSenderAForegroundService(mTarget);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getCachedInfo().getIntentSenderType() == INTENT_SENDER_FOREGROUND_SERVICE;
}
/**
* @return TRUE if this {@link PendingIntent} was created with {@link #getService}.
*/
public boolean isService() {
- try {
- return ActivityManager.getService()
- .isIntentSenderAService(mTarget);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getCachedInfo().getIntentSenderType() == INTENT_SENDER_SERVICE;
}
/**
* @return TRUE if this {@link PendingIntent} was created with {@link #getBroadcast}.
*/
public boolean isBroadcast() {
- try {
- return ActivityManager.getService()
- .isIntentSenderABroadcast(mTarget);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getCachedInfo().getIntentSenderType() == INTENT_SENDER_BROADCAST;
}
/**
@@ -1433,4 +1397,16 @@
*/
void onCancelled(PendingIntent intent);
}
+
+ private PendingIntentInfo getCachedInfo() {
+ if (mCachedInfo == null) {
+ try {
+ mCachedInfo = ActivityManager.getService().getInfoForIntentSender(mTarget);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ return mCachedInfo;
+ }
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 3798de9..2ceea7f 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -697,7 +697,8 @@
* service element of manifest file. The value of attribute
* {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p>
*
- * @throws IllegalStateException If the app targeting API is
+ * @throws ForegroundServiceStartNotAllowedException
+ * If the app targeting API is
* {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from
* becoming foreground service due to background restriction.
*
@@ -738,8 +739,14 @@
* @param notification The Notification to be displayed.
* @param foregroundServiceType must be a subset flags of manifest attribute
* {@link android.R.attr#foregroundServiceType} flags.
+ *
* @throws IllegalArgumentException if param foregroundServiceType is not subset of manifest
* attribute {@link android.R.attr#foregroundServiceType}.
+ * @throws ForegroundServiceStartNotAllowedException
+ * If the app targeting API is
+ * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from
+ * becoming foreground service due to background restriction.
+ *
* @see android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST
*/
public final void startForeground(int id, @NonNull Notification notification,
diff --git a/core/java/android/app/ServiceStartNotAllowedException.java b/core/java/android/app/ServiceStartNotAllowedException.java
new file mode 100644
index 0000000..33285b2
--- /dev/null
+++ b/core/java/android/app/ServiceStartNotAllowedException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.app;
+
+import android.annotation.NonNull;
+
+/**
+ * Exception thrown when an app tries to start a {@link Service} when it's not allowed to do so.
+ */
+public abstract class ServiceStartNotAllowedException extends IllegalStateException {
+ ServiceStartNotAllowedException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Return either {@link ForegroundServiceStartNotAllowedException} or
+ * {@link BackgroundServiceStartNotAllowedException}
+ * @hide
+ */
+ @NonNull
+ public static ServiceStartNotAllowedException newInstance(boolean foreground,
+ @NonNull String message) {
+ if (foreground) {
+ return new ForegroundServiceStartNotAllowedException(message);
+ } else {
+ return new BackgroundServiceStartNotAllowedException(message);
+ }
+ }
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ffaaa57..e16e40b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -47,6 +47,7 @@
import android.app.usage.NetworkStatsManager;
import android.app.usage.StorageStatsManager;
import android.app.usage.UsageStatsManager;
+import android.apphibernation.AppHibernationManager;
import android.appwidget.AppWidgetManager;
import android.bluetooth.BluetoothManager;
import android.companion.CompanionDeviceManager;
@@ -99,6 +100,7 @@
import android.hardware.iris.IIrisService;
import android.hardware.iris.IrisManager;
import android.hardware.lights.LightsManager;
+import android.hardware.lights.SystemLightsManager;
import android.hardware.location.ContextHubManager;
import android.hardware.radio.RadioManager;
import android.hardware.usb.IUsbManager;
@@ -1344,7 +1346,7 @@
@Override
public LightsManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- return new LightsManager(ctx);
+ return new SystemLightsManager(ctx);
}});
registerService(Context.INCREMENTAL_SERVICE, IncrementalManager.class,
new CachedServiceFetcher<IncrementalManager>() {
@@ -1377,6 +1379,13 @@
IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE);
return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b));
}});
+ registerService(Context.APP_HIBERNATION_SERVICE, AppHibernationManager.class,
+ new CachedServiceFetcher<AppHibernationManager>() {
+ @Override
+ public AppHibernationManager createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(Context.APP_HIBERNATION_SERVICE);
+ return b == null ? null : new AppHibernationManager(ctx);
+ }});
registerService(Context.DREAM_SERVICE, DreamManager.class,
new CachedServiceFetcher<DreamManager>() {
@Override
diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java
index 9a53b99..132af4b 100644
--- a/core/java/android/app/people/PeopleSpaceTile.java
+++ b/core/java/android/app/people/PeopleSpaceTile.java
@@ -53,6 +53,7 @@
private boolean mIsImportantConversation;
private String mNotificationKey;
private CharSequence mNotificationContent;
+ private String mNotificationCategory;
private Uri mNotificationDataUri;
private Intent mIntent;
private long mNotificationTimestamp;
@@ -70,6 +71,7 @@
mIsImportantConversation = b.mIsImportantConversation;
mNotificationKey = b.mNotificationKey;
mNotificationContent = b.mNotificationContent;
+ mNotificationCategory = b.mNotificationCategory;
mNotificationDataUri = b.mNotificationDataUri;
mIntent = b.mIntent;
mNotificationTimestamp = b.mNotificationTimestamp;
@@ -129,6 +131,10 @@
return mNotificationContent;
}
+ public String getNotificationCategory() {
+ return mNotificationCategory;
+ }
+
public Uri getNotificationDataUri() {
return mNotificationDataUri;
}
@@ -166,6 +172,7 @@
builder.setIsImportantConversation(mIsImportantConversation);
builder.setNotificationKey(mNotificationKey);
builder.setNotificationContent(mNotificationContent);
+ builder.setNotificationCategory(mNotificationCategory);
builder.setNotificationDataUri(mNotificationDataUri);
builder.setIntent(mIntent);
builder.setNotificationTimestamp(mNotificationTimestamp);
@@ -186,6 +193,7 @@
private boolean mIsImportantConversation;
private String mNotificationKey;
private CharSequence mNotificationContent;
+ private String mNotificationCategory;
private Uri mNotificationDataUri;
private Intent mIntent;
private long mNotificationTimestamp;
@@ -299,6 +307,12 @@
return this;
}
+ /** Sets the associated notification's category. */
+ public Builder setNotificationCategory(String notificationCategory) {
+ mNotificationCategory = notificationCategory;
+ return this;
+ }
+
/** Sets the associated notification's data URI. */
public Builder setNotificationDataUri(Uri notificationDataUri) {
mNotificationDataUri = notificationDataUri;
@@ -342,6 +356,7 @@
mIsImportantConversation = in.readBoolean();
mNotificationKey = in.readString();
mNotificationContent = in.readCharSequence();
+ mNotificationCategory = in.readString();
mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader());
mIntent = in.readParcelable(Intent.class.getClassLoader());
mNotificationTimestamp = in.readLong();
@@ -367,6 +382,7 @@
dest.writeBoolean(mIsImportantConversation);
dest.writeString(mNotificationKey);
dest.writeCharSequence(mNotificationContent);
+ dest.writeString(mNotificationCategory);
dest.writeParcelable(mNotificationDataUri, flags);
dest.writeParcelable(mIntent, flags);
dest.writeLong(mNotificationTimestamp);
diff --git a/core/java/android/app/time/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java
index b566eab..61defb5 100644
--- a/core/java/android/app/time/ExternalTimeSuggestion.java
+++ b/core/java/android/app/time/ExternalTimeSuggestion.java
@@ -16,6 +16,8 @@
package android.app.time;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
@@ -31,9 +33,9 @@
/**
* A time signal from an External source.
*
- * External time suggestions are for use in situations where the Android device is part of a wider
- * network of devices that are required to use a single time source, and where authority for the
- * time is external to the Android device. For example, for the Android Auto use case where the
+ * <p>External time suggestions are for use in situations where the Android device is part of a
+ * wider network of devices that are required to use a single time source, and where authority for
+ * the time is external to the Android device. For example, for the Android Auto use case where the
* Android device is part of a wider in-car network of devices that should display the same time.
*
* <p>Android allows for a single external source for time. If there are several external sources
@@ -49,19 +51,19 @@
* capture the elapsed realtime reference clock, e.g. via {@link SystemClock#elapsedRealtime()},
* when the UTC time is first obtained (usually under a wakelock). This enables Android to adjust
* for latency introduced between suggestion creation and eventual use. Adjustments for other
- * sources of latency, i.e. those before the external time suggestion is created, must be handled
- * by the creator.
+ * sources of latency, i.e. those before the external time suggestion is created, must be handled by
+ * the creator.
*
- * <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds
- * elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the
- * elapsed realtime clock when the {@code utcTime.value} was established.
- * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
- * suggestions cannot be persisted across device resets.
+ * <p>{@code elapsedRealtimeMillis} and {@code suggestionMillis} represent the suggested time.
+ * {@code suggestionMillis} is the number of milliseconds elapsed since 1/1/1970 00:00:00 UTC.
+ * {@code elapsedRealtimeMillis} is the value of the elapsed realtime clock when {@code
+ * suggestionMillis} was established. Note that the elapsed realtime clock is considered accurate
+ * but it is volatile, so time suggestions cannot be persisted across device resets.
*
* <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
* record why the suggestion exists and how it was entered. This information exists only to aid in
- * debugging and therefore is used by {@link #toString()}, but it is not for use in detection
- * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
+ * debugging and therefore is used by {@link #toString()}, but it is not for use in detection logic
+ * and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
*
* @hide
*/
@@ -78,17 +80,28 @@
}
};
- @NonNull private final TimestampedValue<Long> mUtcTime;
- @Nullable private ArrayList<String> mDebugInfo;
+ @NonNull
+ private final TimestampedValue<Long> mUtcTime;
+ @Nullable
+ private ArrayList<String> mDebugInfo;
- public ExternalTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
- mUtcTime = Objects.requireNonNull(utcTime);
- Objects.requireNonNull(utcTime.getValue());
+ /**
+ * Creates a time suggestion cross-referenced to the elapsed realtime clock. See {@link
+ * ExternalTimeSuggestion} for more details.
+ *
+ * @param elapsedRealtimeMillis the elapsed realtime clock reference for the suggestion
+ * @param suggestionMillis the suggested UTC time in milliseconds since the start of the
+ * Unix epoch
+ */
+ public ExternalTimeSuggestion(@ElapsedRealtimeLong long elapsedRealtimeMillis,
+ @CurrentTimeMillisLong long suggestionMillis) {
+ mUtcTime = new TimestampedValue(elapsedRealtimeMillis, suggestionMillis);
}
private static ExternalTimeSuggestion createFromParcel(Parcel in) {
TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
- ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion(utcTime);
+ ExternalTimeSuggestion suggestion =
+ new ExternalTimeSuggestion(utcTime.getReferenceTimeMillis(), utcTime.getValue());
@SuppressWarnings("unchecked")
ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
suggestion.mDebugInfo = debugInfo;
@@ -106,23 +119,31 @@
dest.writeList(mDebugInfo);
}
+ /**
+ * {@hide}
+ */
@NonNull
public TimestampedValue<Long> getUtcTime() {
return mUtcTime;
}
+ /**
+ * Returns information that can be useful for debugging / logging. See {@link #addDebugInfo}.
+ * {@hide}
+ */
@NonNull
public List<String> getDebugInfo() {
return mDebugInfo == null
- ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+ ? Collections.emptyList()
+ : Collections.unmodifiableList(mDebugInfo);
}
/**
* Associates information with the instance that can be useful for debugging / logging. The
- * information is present in {@link #toString()} but is not considered for
- * {@link #equals(Object)} and {@link #hashCode()}.
+ * information is present in {@link #toString()} but is not considered for {@link
+ * #equals(Object)} and {@link #hashCode()}.
*/
- public void addDebugInfo(String... debugInfos) {
+ public void addDebugInfo(@NonNull String... debugInfos) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
}
@@ -148,9 +169,7 @@
@Override
public String toString() {
- return "ExternalTimeSuggestion{"
- + "mUtcTime=" + mUtcTime
- + ", mDebugInfo=" + mDebugInfo
+ return "ExternalTimeSuggestion{" + "mUtcTime=" + mUtcTime + ", mDebugInfo=" + mDebugInfo
+ '}';
}
}
diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java
index 262d244..430960f 100644
--- a/core/java/android/app/time/TimeManager.java
+++ b/core/java/android/app/time/TimeManager.java
@@ -20,6 +20,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.timedetector.ITimeDetectorService;
import android.app.timezonedetector.ITimeZoneDetectorService;
import android.content.Context;
import android.os.RemoteException;
@@ -45,6 +46,7 @@
private final Object mLock = new Object();
private final ITimeZoneDetectorService mITimeZoneDetectorService;
+ private final ITimeDetectorService mITimeDetectorService;
@GuardedBy("mLock")
private ITimeZoneDetectorListener mTimeZoneDetectorReceiver;
@@ -62,6 +64,8 @@
// internal refactoring.
mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
+ mITimeDetectorService = ITimeDetectorService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.TIME_DETECTOR_SERVICE));
}
/**
@@ -214,4 +218,23 @@
}
}
}
+
+ /**
+ * Suggests the current time from an external time source. For example, a form factor-specific
+ * HAL. This time <em>may</em> be used to set the device system clock, depending on the device
+ * configuration and user settings. This method call is processed asynchronously.
+ * See {@link ExternalTimeSuggestion} for more details.
+ * {@hide}
+ */
+ @RequiresPermission(android.Manifest.permission.SET_TIME)
+ public void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion) {
+ if (DEBUG) {
+ Log.d(TAG, "suggestExternalTime called: " + timeSuggestion);
+ }
+ try {
+ mITimeDetectorService.suggestExternalTime(timeSuggestion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 76f3785..52016b6 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
-import android.app.time.ExternalTimeSuggestion;
import android.content.Context;
import android.os.SystemClock;
import android.os.TimestampedValue;
@@ -80,12 +79,4 @@
*/
@RequiresPermission(android.Manifest.permission.SET_TIME)
void suggestGnssTime(GnssTimeSuggestion timeSuggestion);
-
- /**
- * Suggests the time according to an external time source (form factor specific HAL, etc).
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.SET_TIME)
- void suggestExternalTime(ExternalTimeSuggestion timeSuggestion);
}
diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java
index ef818ef..b0aa3c8 100644
--- a/core/java/android/app/timedetector/TimeDetectorImpl.java
+++ b/core/java/android/app/timedetector/TimeDetectorImpl.java
@@ -17,7 +17,6 @@
package android.app.timedetector;
import android.annotation.NonNull;
-import android.app.time.ExternalTimeSuggestion;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -87,16 +86,4 @@
throw e.rethrowFromSystemServer();
}
}
-
- @Override
- public void suggestExternalTime(ExternalTimeSuggestion timeSuggestion) {
- if (DEBUG) {
- Log.d(TAG, "suggestExternalTime called: " + timeSuggestion);
- }
- try {
- mITimeDetectorService.suggestExternalTime(timeSuggestion);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 4277292..fc54c71 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -40,6 +40,7 @@
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -92,7 +93,10 @@
int mLayoutId = -1;
private InteractionHandler mInteractionHandler;
private boolean mOnLightBackground;
- PointF mCurrentSize = null;
+ private PointF mCurrentSize = null;
+ private RemoteViews.ColorResources mColorResources = null;
+ // Stores the last remote views last inflated.
+ private RemoteViews mLastInflatedRemoteViews = null;
private Executor mAsyncExecutor;
private CancellationSignal mLastExecutionSignal;
@@ -358,7 +362,7 @@
PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips);
if (!newSize.equals(mCurrentSize)) {
mCurrentSize = newSize;
- mLayoutId = -1; // Prevents recycling the view.
+ reapplyLastRemoteViews();
}
}
@@ -368,7 +372,7 @@
public void clearCurrentSize() {
if (mCurrentSize != null) {
mCurrentSize = null;
- mLayoutId = -1;
+ reapplyLastRemoteViews();
}
}
@@ -477,10 +481,18 @@
* AppWidget provider. Will animate into these new views as needed
*/
public void updateAppWidget(RemoteViews remoteViews) {
+ this.mLastInflatedRemoteViews = remoteViews;
applyRemoteViews(remoteViews, true);
}
/**
+ * Reapply the last inflated remote views, or the default view is none was inflated.
+ */
+ private void reapplyLastRemoteViews() {
+ applyRemoteViews(mLastInflatedRemoteViews, true);
+ }
+
+ /**
* @hide
*/
protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) {
@@ -518,7 +530,8 @@
// layout matches, try recycling it
if (content == null && layoutId == mLayoutId) {
try {
- remoteViews.reapply(mContext, mView, mInteractionHandler);
+ remoteViews.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
+ mColorResources);
content = mView;
recycled = true;
if (LOGD) Log.d(TAG, "was able to recycle existing layout");
@@ -530,7 +543,8 @@
// Try normal RemoteView inflation
if (content == null) {
try {
- content = remoteViews.apply(mContext, this, mInteractionHandler, mCurrentSize);
+ content = remoteViews.apply(mContext, this, mInteractionHandler,
+ mCurrentSize, mColorResources);
if (LOGD) Log.d(TAG, "had to inflate new layout");
} catch (RuntimeException e) {
exception = e;
@@ -583,7 +597,8 @@
mAsyncExecutor,
new ViewApplyListener(remoteViews, layoutId, true),
mInteractionHandler,
- mCurrentSize);
+ mCurrentSize,
+ mColorResources);
} catch (Exception e) {
// Reapply failed. Try apply
}
@@ -594,7 +609,8 @@
mAsyncExecutor,
new ViewApplyListener(remoteViews, layoutId, false),
mInteractionHandler,
- mCurrentSize);
+ mCurrentSize,
+ mColorResources);
}
}
@@ -662,9 +678,13 @@
protected Context getRemoteContext() {
try {
// Return if cloned successfully, otherwise default
- return mContext.createApplicationContext(
+ Context newContext = mContext.createApplicationContext(
mInfo.providerInfo.applicationInfo,
Context.CONTEXT_RESTRICTED);
+ if (mColorResources != null) {
+ mColorResources.apply(newContext);
+ }
+ return newContext;
} catch (NameNotFoundException e) {
Log.e(TAG, "Package name " + mInfo.providerInfo.packageName + " not found");
return mContext;
@@ -819,4 +839,37 @@
}
};
}
+
+ /**
+ * Set the dynamically overloaded color resources.
+ *
+ * {@code colorMapping} maps a predefined set of color resources to their ARGB
+ * representation. Any entry not in the predefined set of colors will be ignored.
+ *
+ * Calling this method will trigger a full re-inflation of the App Widget.
+ *
+ * The color resources that can be overloaded are the ones whose name is prefixed with
+ * {@code system_primary_}, {@code system_secondary_} or {@code system_neutral_}, for example
+ * {@link android.R.color#system_primary_500}.
+ */
+ public void setColorResources(@NonNull SparseIntArray colorMapping) {
+ mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping);
+ mLayoutId = -1;
+ reapplyLastRemoteViews();
+ }
+
+ /**
+ * Reset the dynamically overloaded resources, reverting to the default values for
+ * all the colors.
+ *
+ * If colors were defined before, calling this method will trigger a full re-inflation of the
+ * App Widget.
+ */
+ public void resetColorResources() {
+ if (mColorResources != null) {
+ mColorResources = null;
+ mLayoutId = -1;
+ reapplyLastRemoteViews();
+ }
+ }
}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index aac8710..38919f6 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -23,6 +23,8 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
import android.app.IServiceConnection;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1095,7 +1097,9 @@
*
* @hide
*/
- public void setBindAppWidgetPermission(String packageName, int userId, boolean permission) {
+ @TestApi
+ public void setBindAppWidgetPermission(
+ @NonNull String packageName, @UserIdInt int userId, boolean permission) {
if (mService == null) {
return;
}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index d893a5e..6ac1c1a 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -332,12 +332,13 @@
/**
* Resource id for the description of the AppWidget.
+ *
* <p>This field corresponds to the <code>android:description</code> attribute in the AppWidget
* meta-data file.
*/
@SuppressLint("MutableBareField")
@IdRes
- public int descriptionResource;
+ public int descriptionRes;
/**
* Flags indicating various features supported by the widget. These are hints to the widget
@@ -385,7 +386,7 @@
this.widgetCategory = in.readInt();
this.providerInfo = in.readTypedObject(ActivityInfo.CREATOR);
this.widgetFeatures = in.readInt();
- this.descriptionResource = in.readInt();
+ this.descriptionRes = in.readInt();
}
/**
@@ -442,14 +443,22 @@
return loadDrawable(context, density, previewImage, false);
}
- /** Loads localized description for the app widget. */
+ /**
+ * Loads localized description for the app widget.
+ *
+ * <p>Description is intended to be displayed in the UI of the widget picker.
+ *
+ * @param context Context for accessing resources.
+ *
+ * @return CharSequence for app widget description for the current locale.
+ */
@Nullable
- public final String loadDescription(@NonNull Context context) {
- if (ResourceId.isValid(descriptionResource)) {
+ public final CharSequence loadDescription(@NonNull Context context) {
+ if (ResourceId.isValid(descriptionRes)) {
return context.getPackageManager()
.getText(
providerInfo.packageName,
- descriptionResource,
+ descriptionRes,
providerInfo.applicationInfo)
.toString()
.trim();
@@ -499,7 +508,7 @@
out.writeInt(this.widgetCategory);
out.writeTypedObject(this.providerInfo, flags);
out.writeInt(this.widgetFeatures);
- out.writeInt(this.descriptionResource);
+ out.writeInt(this.descriptionRes);
}
@Override
@@ -528,7 +537,7 @@
that.widgetCategory = this.widgetCategory;
that.providerInfo = this.providerInfo;
that.widgetFeatures = this.widgetFeatures;
- that.descriptionResource = this.descriptionResource;
+ that.descriptionRes = this.descriptionRes;
return that;
}
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index cd91aa9..53aaae0 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -943,12 +943,13 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
- public boolean setBufferMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) {
- if (VDBG) log("setBufferMillis(" + codec + ", " + value + ")");
+ public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec,
+ int value) {
+ if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
- return service.setBufferMillis(codec, value);
+ return service.setBufferLengthMillis(codec, value);
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ec46da0..8d41572 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3654,12 +3654,12 @@
}
@Override
- public void onDeviceDisconnected(BluetoothDevice device) {
+ public void onDeviceDisconnected(BluetoothDevice device, int hciReason) {
for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry:
mBluetoothConnectionCallbackExecutorMap.entrySet()) {
BluetoothConnectionCallback callback = callbackExecutorEntry.getKey();
Executor executor = callbackExecutorEntry.getValue();
- executor.execute(() -> callback.onDeviceDisconnected(device));
+ executor.execute(() -> callback.onDeviceDisconnected(device, hciReason));
}
}
};
@@ -3764,8 +3764,155 @@
/**
* Callback triggered when a bluetooth device (classic or BLE) is disconnected
* @param device is the disconnected bluetooth device
+ * @param reason is the disconnect reason
*/
- public void onDeviceDisconnected(BluetoothDevice device) {}
+ public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {}
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "REASON_" }, value = {
+ REASON_UNKNOWN,
+ REASON_LOCAL_REQUEST,
+ REASON_REMOTE_REQUEST,
+ REASON_LOCAL_ERROR,
+ REASON_REMOTE_ERROR,
+ REASON_TIMEOUT,
+ REASON_SECURITY,
+ REASON_SYSTEM_POLICY,
+ REASON_RESOURCE_LIMIT_REACHED,
+ REASON_CONNECTION_EXISTS,
+ REASON_BAD_PARAMETERS})
+ public @interface DisconnectReason {}
+
+ /**
+ * Indicates that the ACL disconnected due to an unknown reason.
+ */
+ public static final int REASON_UNKNOWN = 0;
+
+ /**
+ * Indicates that the ACL disconnected due to an explicit request from the local device.
+ * <p>
+ * Example cause: This is a normal disconnect reason, e.g., user/app initiates
+ * disconnection.
+ */
+ public static final int REASON_LOCAL_REQUEST = 1;
+
+ /**
+ * Indicates that the ACL disconnected due to an explicit request from the remote device.
+ * <p>
+ * Example cause: This is a normal disconnect reason, e.g., user/app initiates
+ * disconnection.
+ * <p>
+ * Example solution: The app can also prompt the user to check their remote device.
+ */
+ public static final int REASON_REMOTE_REQUEST = 2;
+
+ /**
+ * Generic disconnect reason indicating the ACL disconnected due to an error on the local
+ * device.
+ * <p>
+ * Example solution: Prompt the user to check their local device (e.g., phone, car
+ * headunit).
+ */
+ public static final int REASON_LOCAL_ERROR = 3;
+
+ /**
+ * Generic disconnect reason indicating the ACL disconnected due to an error on the remote
+ * device.
+ * <p>
+ * Example solution: Prompt the user to check their remote device (e.g., headset, car
+ * headunit, watch).
+ */
+ public static final int REASON_REMOTE_ERROR = 4;
+
+ /**
+ * Indicates that the ACL disconnected due to a timeout.
+ * <p>
+ * Example cause: remote device might be out of range.
+ * <p>
+ * Example solution: Prompt user to verify their remote device is on or in
+ * connection/pairing mode.
+ */
+ public static final int REASON_TIMEOUT = 5;
+
+ /**
+ * Indicates that the ACL disconnected due to link key issues.
+ * <p>
+ * Example cause: Devices are either unpaired or remote device is refusing our pairing
+ * request.
+ * <p>
+ * Example solution: Prompt user to unpair and pair again.
+ */
+ public static final int REASON_SECURITY = 6;
+
+ /**
+ * Indicates that the ACL disconnected due to the local device's system policy.
+ * <p>
+ * Example cause: privacy policy, power management policy, permissions, etc.
+ * <p>
+ * Example solution: Prompt the user to check settings, or check with their system
+ * administrator (e.g. some corp-managed devices do not allow OPP connection).
+ */
+ public static final int REASON_SYSTEM_POLICY = 7;
+
+ /**
+ * Indicates that the ACL disconnected due to resource constraints, either on the local
+ * device or the remote device.
+ * <p>
+ * Example cause: controller is busy, memory limit reached, maximum number of connections
+ * reached.
+ * <p>
+ * Example solution: The app should wait and try again. If still failing, prompt the user
+ * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device.
+ */
+ public static final int REASON_RESOURCE_LIMIT_REACHED = 8;
+
+ /**
+ * Indicates that the ACL disconnected because another ACL connection already exists.
+ */
+ public static final int REASON_CONNECTION_EXISTS = 9;
+
+ /**
+ * Indicates that the ACL disconnected due to incorrect parameters passed in from the app.
+ * <p>
+ * Example solution: Change parameters and try again. If error persists, the app can report
+ * telemetry and/or log the error in a bugreport.
+ */
+ public static final int REASON_BAD_PARAMETERS = 10;
+
+ /**
+ * Returns human-readable strings corresponding to {@link DisconnectReason}.
+ */
+ public static String disconnectReasonText(@DisconnectReason int reason) {
+ switch (reason) {
+ case REASON_UNKNOWN:
+ return "Reason unknown";
+ case REASON_LOCAL_REQUEST:
+ return "Local request";
+ case REASON_REMOTE_REQUEST:
+ return "Remote request";
+ case REASON_LOCAL_ERROR:
+ return "Local error";
+ case REASON_REMOTE_ERROR:
+ return "Remote error";
+ case REASON_TIMEOUT:
+ return "Timeout";
+ case REASON_SECURITY:
+ return "Security";
+ case REASON_SYSTEM_POLICY:
+ return "System policy";
+ case REASON_RESOURCE_LIMIT_REACHED:
+ return "Resource constrained";
+ case REASON_CONNECTION_EXISTS:
+ return "Connection already exists";
+ case REASON_BAD_PARAMETERS:
+ return "Bad parameters";
+ default:
+ return "Unrecognized disconnect reason: " + reason;
+ }
+ }
}
/**
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index ff6cffb..0312a21 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -18,7 +18,9 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -30,6 +32,7 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
/**
@@ -37,44 +40,60 @@
*
* @hide
*/
+@SystemApi
public final class BluetoothMapClient implements BluetoothProfile {
private static final String TAG = "BluetoothMapClient";
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
+ /** @hide */
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
+ /** @hide */
public static final String ACTION_MESSAGE_RECEIVED =
"android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED";
/* Actions to be used for pending intents */
+ /** @hide */
public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY =
"android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY";
+ /** @hide */
public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
"android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
/**
* Action to notify read status changed
+ *
+ * @hide
*/
public static final String ACTION_MESSAGE_READ_STATUS_CHANGED =
"android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED";
/**
* Action to notify deleted status changed
+ *
+ * @hide
*/
public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED =
"android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED";
- /* Extras used in ACTION_MESSAGE_RECEIVED intent.
- * NOTE: HANDLE is only valid for a single session with the device. */
+ /**
+ * Extras used in ACTION_MESSAGE_RECEIVED intent.
+ * NOTE: HANDLE is only valid for a single session with the device.
+ */
+ /** @hide */
public static final String EXTRA_MESSAGE_HANDLE =
"android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE";
+ /** @hide */
public static final String EXTRA_MESSAGE_TIMESTAMP =
"android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP";
+ /** @hide */
public static final String EXTRA_MESSAGE_READ_STATUS =
"android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS";
+ /** @hide */
public static final String EXTRA_SENDER_CONTACT_URI =
"android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI";
+ /** @hide */
public static final String EXTRA_SENDER_CONTACT_NAME =
"android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
@@ -84,6 +103,8 @@
* Possible values are:
* true: deleted
* false: undeleted
+ *
+ * @hide
*/
public static final String EXTRA_MESSAGE_DELETED_STATUS =
"android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS";
@@ -93,24 +114,42 @@
* Possible values are:
* 0: failure
* 1: success
+ *
+ * @hide
*/
public static final String EXTRA_RESULT_CODE =
"android.bluetooth.device.extra.RESULT_CODE";
- /** There was an error trying to obtain the state */
+ /**
+ * There was an error trying to obtain the state
+ * @hide
+ */
public static final int STATE_ERROR = -1;
+ /** @hide */
public static final int RESULT_FAILURE = 0;
+ /** @hide */
public static final int RESULT_SUCCESS = 1;
- /** Connection canceled before completion. */
+ /**
+ * Connection canceled before completion.
+ * @hide
+ */
public static final int RESULT_CANCELED = 2;
-
+ /** @hide */
private static final int UPLOADING_FEATURE_BITMASK = 0x08;
- /** Parameters in setMessageStatus */
+ /*
+ * UNREAD, READ, UNDELETED, DELETED are passed as parameters
+ * to setMessageStatus to indicate the messages new state.
+ */
+
+ /** @hide */
public static final int UNREAD = 0;
+ /** @hide */
public static final int READ = 1;
+ /** @hide */
public static final int UNDELETED = 2;
+ /** @hide */
public static final int DELETED = 3;
private BluetoothAdapter mAdapter;
@@ -132,19 +171,12 @@
mProfileConnector.connect(context, listener);
}
- protected void finalize() throws Throwable {
- try {
- close();
- } finally {
- super.finalize();
- }
- }
-
/**
* Close the connection to the backing service.
* Other public functions of BluetoothMap will return default error
* results once close() has been called. Multiple invocations of close()
* are ok.
+ * @hide
*/
public void close() {
mProfileConnector.disconnect();
@@ -158,6 +190,7 @@
* Returns true if the specified Bluetooth device is connected.
* Returns false if not connected, or if this proxy object is not
* currently connected to the Map service.
+ * @hide
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
@@ -225,6 +258,7 @@
* Get the list of connected devices. Currently at most one.
*
* @return list of connected devices
+ * @hide
*/
@Override
public List<BluetoothDevice> getConnectedDevices() {
@@ -246,6 +280,7 @@
* Get the list of devices matching specified states. Currently at most one.
*
* @return list of matching devices
+ * @hide
*/
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
@@ -267,6 +302,7 @@
* Get connection state of device
*
* @return device connection state
+ * @hide
*/
@Override
public int getConnectionState(BluetoothDevice device) {
@@ -383,11 +419,44 @@
* Send an SMS message to either the contacts primary number or the telephone number specified.
*
* @param device Bluetooth device
+ * @param contacts Uri Collection of the contacts
+ * @param message Message to be sent
+ * @param sentIntent intent issued when message is sent
+ * @param deliveredIntent intent issued when message is delivered
+ * @return true if the message is enqueued, false on error
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.SEND_SMS)
+ public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts,
+ @NonNull String message, @Nullable PendingIntent sentIntent,
+ @Nullable PendingIntent deliveredIntent) {
+ if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
+ final IBluetoothMapClient service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]),
+ message, sentIntent, deliveredIntent);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Send a message.
+ *
+ * Send an SMS message to either the contacts primary number or the telephone number specified.
+ *
+ * @param device Bluetooth device
* @param contacts Uri[] of the contacts
* @param message Message to be sent
* @param sentIntent intent issued when message is sent
* @param deliveredIntent intent issued when message is delivered
* @return true if the message is enqueued, false on error
+ * @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
@@ -410,6 +479,7 @@
*
* @param device Bluetooth device
* @return true if the message is enqueued, false on error
+ * @hide
*/
public boolean getUnreadMessages(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
@@ -431,6 +501,7 @@
* @param device The Bluetooth device to get this value for.
* @return Returns true if the Uploading bit value in SDP record's
* MapSupportedFeatures field is set. False is returned otherwise.
+ * @hide
*/
public boolean isUploadingSupported(BluetoothDevice device) {
final IBluetoothMapClient service = getService();
@@ -457,7 +528,7 @@
* "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for
* "deleted", otherwise return error
* @return <code>true</code> if request has been sent, <code>false</code> on error
- *
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_SMS)
public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index c31b04e..201d6c4 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -186,6 +186,7 @@
*
* @hide
*/
+ @SystemApi
int MAP_CLIENT = 18;
/**
diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java
index 7e5ec1e..97d9723 100644
--- a/core/java/android/bluetooth/BufferConstraints.java
+++ b/core/java/android/bluetooth/BufferConstraints.java
@@ -90,7 +90,7 @@
* @hide
*/
@SystemApi
- public @Nullable BufferConstraint getCodec(@BluetoothCodecConfig.SourceCodecType int codec) {
+ public @Nullable BufferConstraint forCodec(@BluetoothCodecConfig.SourceCodecType int codec) {
return mBufferConstraints.get(codec);
}
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 46d8900..230c985 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -132,8 +132,11 @@
public static final String SYNC_EXTRAS_ACCOUNT = "account";
/**
- * If this extra is set to true, the sync request will be scheduled
- * at the front of the sync request queue and without any delay
+ * If this extra is set to true, the sync request will be scheduled at the front of the
+ * sync request queue, but it is still subject to JobScheduler quota and throttling due to
+ * App Standby buckets.
+ *
+ * <p>This is different from {@link #SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}.
*/
public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
@@ -145,6 +148,29 @@
public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
/**
+ * Run this sync operation as an "expedited job"
+ * (see {@link android.app.job.JobInfo.Builder#setExpedited(boolean)}).
+ * Normally (if this flag isn't specified), sync operations are executed as regular
+ * {@link android.app.job.JobService} jobs.
+ *
+ * <p> Because Expedited Jobs have various restrictions compared to regular jobs, this flag
+ * cannot be combined with certain other flags, otherwise an
+ * <code>IllegalArgumentException</code> will be thrown. Notably, because Expedited Jobs do not
+ * support various constraints, the following restriction apply:
+ * <ul>
+ * <li>Can't be used with {@link #SYNC_EXTRAS_REQUIRE_CHARGING}
+ * <li>Can't be used with {@link #SYNC_EXTRAS_EXPEDITED}
+ * <li>Can't be used on periodic syncs.
+ * <li>When an expedited-job-sync fails and a retry is scheduled, the retried sync will be
+ * scheduled as a regular job unless {@link #SYNC_EXTRAS_IGNORE_BACKOFF} is set.
+ * </ul>
+ *
+ * <p>This is different from {@link #SYNC_EXTRAS_EXPEDITED}.
+ */
+ @SuppressLint("IntentName")
+ public static final String SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB = "schedule_as_expedited_job";
+
+ /**
* @deprecated instead use
* {@link #SYNC_EXTRAS_MANUAL}
*/
@@ -3220,6 +3246,18 @@
}
/**
+ * {@hide}
+ * Helper function to throw an <code>IllegalArgumentException</code> if any illegal
+ * extras were set for a sync scheduled as an expedited job.
+ *
+ * @param extras bundle to validate.
+ */
+ public static boolean hasInvalidScheduleAsEjExtras(Bundle extras) {
+ return extras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING)
+ || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED);
+ }
+
+ /**
* Specifies that a sync should be requested with the specified the account, authority,
* and extras at the given frequency. If there is already another periodic sync scheduled
* with the account, authority and extras then a new periodic sync won't be added, instead
@@ -3233,7 +3271,8 @@
* Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY},
* {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS},
* {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE},
- * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true.
+ * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL},
+ * {@link #SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB} set to true.
* If any are supplied then an {@link IllegalArgumentException} will be thrown.
*
* <p>This method requires the caller to hold the permission
@@ -3273,16 +3312,14 @@
* @param extras bundle to validate.
*/
public static boolean invalidPeriodicExtras(Bundle extras) {
- if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)
+ return extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)
|| extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)
|| extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)
|| extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
|| extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
|| extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false)
- || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
- return true;
- }
- return false;
+ || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)
+ || extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false);
}
/**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4284dc2..e20f706 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3118,8 +3118,18 @@
*
* @throws SecurityException If the caller does not have permission to access the service
* or the service can not be found.
- * @throws IllegalStateException If the application is in a state where the service
- * can not be started (such as not in the foreground in a state when services are allowed).
+ * @throws IllegalStateException
+ * Before Android {@link android.os.Build.VERSION_CODES#S},
+ * if the application is in a state where the service
+ * can not be started (such as not in the foreground in a state when services are allowed),
+ * {@link IllegalStateException} was thrown.
+ * @throws android.app.BackgroundServiceStartNotAllowedException
+ * On Android {@link android.os.Build.VERSION_CODES#S} and later,
+ * if the application is in a state where the service
+ * can not be started (such as not in the foreground in a state when services are allowed),
+ * {@link android.app.BackgroundServiceStartNotAllowedException} is thrown
+ * This excemption extends {@link IllegalStateException}, so apps can
+ * use {@code catch (IllegalStateException)} to catch both.
*
* @see #stopService
* @see #bindService
@@ -3150,7 +3160,8 @@
* @throws SecurityException If the caller does not have permission to access the service
* or the service can not be found.
*
- * @throws IllegalStateException If the caller app's targeting API is
+ * @throws android.app.ForegroundServiceStartNotAllowedException
+ * If the caller app's targeting API is
* {@link android.os.Build.VERSION_CODES#S} or later, and the foreground service is restricted
* from start due to background restriction.
*
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 858d1e4..b1252fd 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManager.PendingIntentInfo;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.Handler;
@@ -60,6 +61,9 @@
private final IIntentSender mTarget;
IBinder mWhitelistToken;
+ // cached pending intent information
+ private @Nullable PendingIntentInfo mCachedInfo;
+
/**
* Exception thrown when trying to send through a PendingIntent that
* has been canceled or is otherwise no longer able to execute the request.
@@ -209,13 +213,7 @@
*/
@Deprecated
public String getTargetPackage() {
- try {
- return ActivityManager.getService()
- .getPackageForIntentSender(mTarget);
- } catch (RemoteException e) {
- // Should never happen.
- return null;
- }
+ return getCreatorPackage();
}
/**
@@ -228,13 +226,7 @@
* none associated with it.
*/
public String getCreatorPackage() {
- try {
- return ActivityManager.getService()
- .getPackageForIntentSender(mTarget);
- } catch (RemoteException e) {
- // Should never happen.
- return null;
- }
+ return getCachedInfo().getCreatorPackage();
}
/**
@@ -247,13 +239,7 @@
* none associated with it.
*/
public int getCreatorUid() {
- try {
- return ActivityManager.getService()
- .getUidForIntentSender(mTarget);
- } catch (RemoteException e) {
- // Should never happen.
- return -1;
- }
+ return getCachedInfo().getCreatorUid();
}
/**
@@ -268,14 +254,8 @@
* none associated with it.
*/
public UserHandle getCreatorUserHandle() {
- try {
- int uid = ActivityManager.getService()
- .getUidForIntentSender(mTarget);
- return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
- } catch (RemoteException e) {
- // Should never happen.
- return null;
- }
+ int uid = getCachedInfo().getCreatorUid();
+ return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
}
/**
@@ -384,4 +364,16 @@
public IntentSender(IBinder target) {
mTarget = IIntentSender.Stub.asInterface(target);
}
+
+ private PendingIntentInfo getCachedInfo() {
+ if (mCachedInfo == null) {
+ try {
+ mCachedInfo = ActivityManager.getService().getInfoForIntentSender(mTarget);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ return mCachedInfo;
+ }
}
diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java
index 9e568a4..e1e6f75 100644
--- a/core/java/android/content/SyncRequest.java
+++ b/core/java/android/content/SyncRequest.java
@@ -17,6 +17,7 @@
package android.content;
import android.accounts.Account;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
@@ -58,6 +59,8 @@
private final boolean mIsAuthority;
/** Sync should be run in lieu of other syncs. */
private final boolean mIsExpedited;
+ /** Sync sound be ran as an expedited job. */
+ private final boolean mIsScheduledAsExpeditedJob;
/**
* {@hide}
@@ -79,6 +82,14 @@
/**
* {@hide}
+ * @return whether this sync is scheduled as an expedited job.
+ */
+ public boolean isScheduledAsExpeditedJob() {
+ return mIsScheduledAsExpeditedJob;
+ }
+
+ /**
+ * {@hide}
*
* @return account object for this sync.
* @throws IllegalArgumentException if this function is called for a request that targets a
@@ -149,6 +160,7 @@
parcel.writeInt((mDisallowMetered ? 1 : 0));
parcel.writeInt((mIsAuthority ? 1 : 0));
parcel.writeInt((mIsExpedited? 1 : 0));
+ parcel.writeInt(mIsScheduledAsExpeditedJob ? 1 : 0);
parcel.writeParcelable(mAccountToSync, flags);
parcel.writeString(mAuthority);
}
@@ -161,6 +173,7 @@
mDisallowMetered = (in.readInt() != 0);
mIsAuthority = (in.readInt() != 0);
mIsExpedited = (in.readInt() != 0);
+ mIsScheduledAsExpeditedJob = (in.readInt() != 0);
mAccountToSync = in.readParcelable(null);
mAuthority = in.readString();
}
@@ -174,6 +187,7 @@
mIsPeriodic = (b.mSyncType == Builder.SYNC_TYPE_PERIODIC);
mIsAuthority = (b.mSyncTarget == Builder.SYNC_TARGET_ADAPTER);
mIsExpedited = b.mExpedited;
+ mIsScheduledAsExpeditedJob = b.mScheduleAsExpeditedJob;
mExtras = new Bundle(b.mCustomExtras);
// For now we merge the sync config extras & the custom extras into one bundle.
// TODO: pass the configuration extras through separately.
@@ -258,6 +272,11 @@
*/
private boolean mRequiresCharging;
+ /**
+ * Whether the sync should be scheduled as an expedited job.
+ */
+ private boolean mScheduleAsExpeditedJob;
+
public Builder() {
}
@@ -309,7 +328,8 @@
* {@link ContentResolver#SYNC_EXTRAS_INITIALIZE},
* {@link ContentResolver#SYNC_EXTRAS_FORCE},
* {@link ContentResolver#SYNC_EXTRAS_EXPEDITED},
- * {@link ContentResolver#SYNC_EXTRAS_MANUAL}
+ * {@link ContentResolver#SYNC_EXTRAS_MANUAL},
+ * {@link ContentResolver#SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}
* set to true. If any are supplied then an <code>IllegalArgumentException</code> will
* be thrown.
*
@@ -500,6 +520,22 @@
}
/**
+ * Convenience function for setting
+ * {@link ContentResolver#SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}.
+ *
+ * <p> Not to be confused with {@link ContentResolver#SYNC_EXTRAS_EXPEDITED}.
+ *
+ * <p> Not valid for periodic syncs, expedited syncs, and syncs that require charging - an
+ * <code>IllegalArgumentException</code> will be thrown in {@link #build()}.
+ *
+ * @param scheduleAsExpeditedJob whether to schedule as an expedited job. Default false.
+ */
+ public @NonNull Builder setScheduleAsExpeditedJob(boolean scheduleAsExpeditedJob) {
+ mScheduleAsExpeditedJob = scheduleAsExpeditedJob;
+ return this;
+ }
+
+ /**
* Performs validation over the request and throws the runtime exception
* <code>IllegalArgumentException</code> if this validation fails.
*
@@ -507,11 +543,6 @@
* builder.
*/
public SyncRequest build() {
- // Validate the extras bundle
- ContentResolver.validateSyncExtrasBundle(mCustomExtras);
- if (mCustomExtras == null) {
- mCustomExtras = new Bundle();
- }
// Combine builder extra flags into the config bundle.
mSyncConfigExtras = new Bundle();
if (mIgnoreBackoff) {
@@ -532,17 +563,35 @@
if (mExpedited) {
mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
}
+ if (mScheduleAsExpeditedJob) {
+ mSyncConfigExtras.putBoolean(
+ ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true);
+ }
if (mIsManual) {
mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
}
+
+ if (mCustomExtras == null) {
+ mCustomExtras = new Bundle();
+ }
+ // Validate the extras bundles
+ ContentResolver.validateSyncExtrasBundle(mCustomExtras);
+ // If this is a periodic sync ensure than invalid extras were not set.
if (mSyncType == SYNC_TYPE_PERIODIC) {
- // If this is a periodic sync ensure than invalid extras were not set.
if (ContentResolver.invalidPeriodicExtras(mCustomExtras) ||
ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) {
throw new IllegalArgumentException("Illegal extras were set");
}
}
+ // If this sync is scheduled as an EJ, ensure that invalid extras were not set.
+ if (mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)
+ || mScheduleAsExpeditedJob) {
+ if (ContentResolver.hasInvalidScheduleAsEjExtras(mCustomExtras)
+ || ContentResolver.hasInvalidScheduleAsEjExtras(mSyncConfigExtras)) {
+ throw new IllegalArgumentException("Illegal extras were set");
+ }
+ }
// Ensure that a target for the sync has been set.
if (mSyncTarget == SYNC_TARGET_UNKNOWN) {
throw new IllegalArgumentException("Must specify an adapter with" +
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 01ff432..dec2c3d 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1105,19 +1105,15 @@
* <p>
* This property is the compile-time equivalent of
* {@link android.os.Build.VERSION#CODENAME Build.VERSION.SDK_INT}.
- *
- * @hide For platform use only; we don't expect developers to need to read this value.
*/
public int compileSdkVersion;
/**
- * The development codename (ex. "O", "REL") of the framework against which the application
+ * The development codename (ex. "S", "REL") of the framework against which the application
* claims to have been compiled, or {@code null} if not specified.
* <p>
* This property is the compile-time equivalent of
* {@link android.os.Build.VERSION#CODENAME Build.VERSION.CODENAME}.
- *
- * @hide For platform use only; we don't expect developers to need to read this value.
*/
@Nullable
public String compileSdkVersionCodename;
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index bc5d14a..0d5b33c 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -24,16 +24,16 @@
import android.content.pm.parsing.PackageLite;
import android.os.SystemProperties;
import android.util.ArrayMap;
-import android.util.jar.StrictJarFile;
import android.util.JsonReader;
import android.util.Log;
+import android.util.jar.StrictJarFile;
import com.android.internal.annotations.VisibleForTesting;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Paths;
@@ -53,7 +53,10 @@
/** $> adb shell 'setprop log.tag.DexMetadataHelper VERBOSE' */
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
/** $> adb shell 'setprop pm.dexopt.dm.require_manifest true' */
- private static String PROPERTY_DM_JSON_MANIFEST_REQUIRED = "pm.dexopt.dm.require_manifest";
+ private static final String PROPERTY_DM_JSON_MANIFEST_REQUIRED =
+ "pm.dexopt.dm.require_manifest";
+ /** $> adb shell 'setprop pm.dexopt.dm.require_fsverity true' */
+ private static final String PROPERTY_DM_FSVERITY_REQUIRED = "pm.dexopt.dm.require_fsverity";
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
@@ -70,6 +73,13 @@
}
/**
+ * Returns whether fs-verity is required to install a dex metadata
+ */
+ public static boolean isFsVerityRequired() {
+ return SystemProperties.getBoolean(PROPERTY_DM_FSVERITY_REQUIRED, false);
+ }
+
+ /**
* Return the size (in bytes) of all dex metadata files associated with the given package.
*/
public static long getPackageDexMetadataSize(PackageLite pkg) {
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 1ffd18f..788afe3 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -667,7 +667,7 @@
private abstract static class BaseEventQueue {
private static native long nativeInitBaseEventQueue(long nativeManager,
WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ,
- String packageName, int mode, String opPackageName);
+ String packageName, int mode, String opPackageName, String attributionTag);
private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
int maxBatchReportLatencyUs);
private static native int nativeDisableSensor(long eventQ, int handle);
@@ -689,7 +689,8 @@
if (packageName == null) packageName = "";
mNativeSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance,
new WeakReference<>(this), looper.getQueue(),
- packageName, mode, manager.mContext.getOpPackageName());
+ packageName, mode, manager.mContext.getOpPackageName(),
+ manager.mContext.getAttributionTag());
mCloseGuard.open("dispose");
mManager = manager;
}
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 76d50bd..43ef33e 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -145,6 +145,12 @@
int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
/**
+ * Authentication cannot proceed because re-enrollment is required.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_RE_ENROLL = 16;
+
+ /**
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
* because the authentication attempt was unsuccessful.
* @hide
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index eafcf52..4385b1da 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -153,6 +153,12 @@
int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
/**
+ * Authentication cannot proceed because re-enrollment is required.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_RE_ENROLL = 16;
+
+ /**
* @hide
*/
int FACE_ERROR_VENDOR_BASE = 1000;
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 01f0e71..30e24d2e 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -166,6 +166,12 @@
public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
/**
+ * Authentication cannot proceed because re-enrollment is required.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_RE_ENROLL = 16;
+
+ /**
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index f9eecae..ac6ba0a 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -378,9 +378,10 @@
* released, continuous repeating requests stopped and any pending
* multi-frame capture requests flushed.</p>
*
- * <p>Note that the CameraExtensionSession currently supports at most two
- * multi frame capture surface formats: ImageFormat.YUV_420_888 and
- * ImageFormat.JPEG. Clients must query the multi-frame capture format support using
+ * <p>Note that the CameraExtensionSession currently supports at most wo
+ * multi frame capture surface formats: ImageFormat.JPEG will be supported
+ * by all extensions and ImageFormat.YUV_420_888 may or may not be supported.
+ * Clients must query the multi-frame capture format support using
* {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, int)}.
* For repeating requests CameraExtensionSession supports only
* {@link android.graphics.SurfaceTexture} as output. Clients can query the supported resolution
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index d3eb377..6121cd2 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -35,6 +35,7 @@
import android.util.Pair;
import android.util.Size;
+import java.util.HashSet;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -153,12 +154,8 @@
mChars = chars;
}
- private static List<Size> generateSupportedSizes(List<SizeList> sizesList,
- Integer format,
- StreamConfigurationMap streamMap) {
- // Per API contract it is assumed that the extension is able to support all
- // camera advertised sizes for a given format in case it doesn't return
- // a valid non-empty size list.
+ private static ArrayList<Size> getSupportedSizes(List<SizeList> sizesList,
+ Integer format) {
ArrayList<Size> ret = new ArrayList<>();
if ((sizesList != null) && (!sizesList.isEmpty())) {
for (SizeList entry : sizesList) {
@@ -170,13 +167,36 @@
}
}
}
+
+ return ret;
+ }
+
+ private static List<Size> generateSupportedSizes(List<SizeList> sizesList,
+ Integer format,
+ StreamConfigurationMap streamMap) {
+ // Per API contract it is assumed that the extension is able to support all
+ // camera advertised sizes for a given format in case it doesn't return
+ // a valid non-empty size list.
+ ArrayList<Size> ret = getSupportedSizes(sizesList, format);
Size[] supportedSizes = streamMap.getOutputSizes(format);
- if (supportedSizes != null) {
+ if ((ret.isEmpty()) && (supportedSizes != null)) {
ret.addAll(Arrays.asList(supportedSizes));
}
return ret;
}
+ private static List<Size> generateJpegSupportedSizes(List<SizeList> sizesList,
+ StreamConfigurationMap streamMap) {
+ ArrayList<Size> extensionSizes = getSupportedSizes(sizesList, ImageFormat.YUV_420_888);
+ HashSet<Size> supportedSizes = extensionSizes.isEmpty() ? new HashSet<>(Arrays.asList(
+ streamMap.getOutputSizes(ImageFormat.YUV_420_888))) : new HashSet<>(extensionSizes);
+ HashSet<Size> supportedJpegSizes = new HashSet<>(Arrays.asList(streamMap.getOutputSizes(
+ ImageFormat.JPEG)));
+ supportedSizes.retainAll(supportedJpegSizes);
+
+ return new ArrayList<>(supportedSizes);
+ }
+
/**
* A per-process global camera extension manager instance, to track and
* initialize/release extensions depending on client activity.
@@ -488,8 +508,8 @@
* {@link StreamConfigurationMap#getOutputSizes}.</p>
*
* <p>Device-specific extensions currently support at most two
- * multi-frame capture surface formats, ImageFormat.YUV_420_888 or
- * ImageFormat.JPEG.</p>
+ * multi-frame capture surface formats. ImageFormat.JPEG will be supported by all
+ * extensions and ImageFormat.YUV_420_888 may or may not be supported.</p>
*
* @param extension the extension type
* @param format device-specific extension output format
@@ -526,14 +546,17 @@
format, streamMap);
} else if (format == ImageFormat.JPEG) {
extenders.second.init(mCameraId, mChars.getNativeMetadata());
- if (extenders.second.getCaptureProcessor() == null) {
+ if (extenders.second.getCaptureProcessor() != null) {
+ // The framework will perform the additional encoding pass on the
+ // processed YUV_420 buffers.
+ return generateJpegSupportedSizes(
+ extenders.second.getSupportedResolutions(), streamMap);
+ } else {
return generateSupportedSizes(null, format, streamMap);
}
-
- return new ArrayList<>();
+ } else {
+ throw new IllegalArgumentException("Unsupported format: " + format);
}
-
- throw new IllegalArgumentException("Unsupported format: " + format);
} finally {
unregisterClient(clientId);
}
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index 877dfbc..e1b8177 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -238,8 +238,10 @@
* from the camera device, to produce a single high-quality output result.
*
* <p>Note that single capture requests currently do not support
- * client parameters. Settings included in the request will
- * be entirely overridden by the device-specific extension. </p>
+ * client parameters except for {@link CaptureRequest#JPEG_ORIENTATION orientation} and
+ * {@link CaptureRequest#JPEG_QUALITY quality} in case of ImageFormat.JPEG output target.
+ * The rest of the settings included in the request will be entirely overridden by
+ * the device-specific extension. </p>
*
* <p>The {@link CaptureRequest.Builder#addTarget} supports only one
* ImageFormat.YUV_420_888 or ImageFormat.JPEG target surface. {@link CaptureRequest}
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
new file mode 100644
index 0000000..936734b
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -0,0 +1,312 @@
+/*
+ * 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.hardware.camera2.impl;
+
+import android.annotation.NonNull;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.extension.CaptureBundle;
+import android.hardware.camera2.extension.ICaptureProcessorImpl;
+import android.media.Image;
+import android.media.Image.Plane;
+import android.media.ImageReader;
+import android.media.ImageWriter;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+// Jpeg compress input YUV and queue back in the client target surface.
+public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
+ public final static String TAG = "CameraExtensionJpeg";
+ private final static int JPEG_QUEUE_SIZE = 1;
+ private final static int JPEG_DEFAULT_QUALITY = 100;
+ private final static int JPEG_DEFAULT_ROTATION = 0;
+
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread;
+ private final ICaptureProcessorImpl mProcessor;
+
+ private ImageReader mYuvReader = null;
+ private android.hardware.camera2.extension.Size mResolution = null;
+ private int mFormat = -1;
+ private Surface mOutputSurface = null;
+ private ImageWriter mOutputWriter = null;
+
+ private static final class JpegParameters {
+ public HashSet<Long> mTimeStamps = new HashSet<>();
+ public int mRotation = JPEG_DEFAULT_ROTATION; // CCW multiple of 90 degrees
+ public int mQuality = JPEG_DEFAULT_QUALITY; // [0..100]
+ }
+
+ private ConcurrentLinkedQueue<JpegParameters> mJpegParameters = new ConcurrentLinkedQueue<>();
+
+ public CameraExtensionJpegProcessor(@NonNull ICaptureProcessorImpl processor) {
+ mProcessor = processor;
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ public void close() {
+ mHandlerThread.quitSafely();
+
+ if (mOutputWriter != null) {
+ mOutputWriter.close();
+ mOutputWriter = null;
+ }
+
+ if (mYuvReader != null) {
+ mYuvReader.close();
+ mYuvReader = null;
+ }
+ }
+
+ private static JpegParameters getJpegParameters(List<CaptureBundle> captureBundles) {
+ JpegParameters ret = new JpegParameters();
+ if (!captureBundles.isEmpty()) {
+ // The quality and orientation settings must be equal for requests in a burst
+
+ Byte jpegQuality = captureBundles.get(0).captureResult.get(CaptureResult.JPEG_QUALITY);
+ if (jpegQuality != null) {
+ ret.mQuality = jpegQuality;
+ } else {
+ Log.w(TAG, "No jpeg quality set, using default: " + JPEG_DEFAULT_QUALITY);
+ }
+
+ Integer orientation = captureBundles.get(0).captureResult.get(
+ CaptureResult.JPEG_ORIENTATION);
+ if (orientation != null) {
+ ret.mRotation = orientation / 90;
+ } else {
+ Log.w(TAG, "No jpeg rotation set, using default: " + JPEG_DEFAULT_ROTATION);
+ }
+
+ for (CaptureBundle bundle : captureBundles) {
+ Long timeStamp = bundle.captureResult.get(CaptureResult.SENSOR_TIMESTAMP);
+ if (timeStamp != null) {
+ ret.mTimeStamps.add(timeStamp);
+ } else {
+ Log.e(TAG, "Capture bundle without valid sensor timestamp!");
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Compresses a YCbCr image to jpeg, applying a crop and rotation.
+ * <p>
+ * The input is defined as a set of 3 planes of 8-bit samples, one plane for
+ * each channel of Y, Cb, Cr.<br>
+ * The Y plane is assumed to have the same width and height of the entire
+ * image.<br>
+ * The Cb and Cr planes are assumed to be downsampled by a factor of 2, to
+ * have dimensions (floor(width / 2), floor(height / 2)).<br>
+ * Each plane is specified by a direct java.nio.ByteBuffer, a pixel-stride,
+ * and a row-stride. So, the sample at coordinate (x, y) can be retrieved
+ * from byteBuffer[x * pixel_stride + y * row_stride].
+ * <p>
+ * The pre-compression transformation is applied as follows:
+ * <ol>
+ * <li>The image is cropped to the rectangle from (cropLeft, cropTop) to
+ * (cropRight - 1, cropBottom - 1). So, a cropping-rectangle of (0, 0) -
+ * (width, height) is a no-op.</li>
+ * <li>The rotation is applied counter-clockwise relative to the coordinate
+ * space of the image, so a CCW rotation will appear CW when the image is
+ * rendered in scanline order. Only rotations which are multiples of
+ * 90-degrees are suppored, so the parameter 'rot90' specifies which
+ * multiple of 90 to rotate the image.</li>
+ * </ol>
+ *
+ * @param width the width of the image to compress
+ * @param height the height of the image to compress
+ * @param yBuf the buffer containing the Y component of the image
+ * @param yPStride the stride between adjacent pixels in the same row in
+ * yBuf
+ * @param yRStride the stride between adjacent rows in yBuf
+ * @param cbBuf the buffer containing the Cb component of the image
+ * @param cbPStride the stride between adjacent pixels in the same row in
+ * cbBuf
+ * @param cbRStride the stride between adjacent rows in cbBuf
+ * @param crBuf the buffer containing the Cr component of the image
+ * @param crPStride the stride between adjacent pixels in the same row in
+ * crBuf
+ * @param crRStride the stride between adjacent rows in crBuf
+ * @param outBuf a direct java.nio.ByteBuffer to hold the compressed jpeg.
+ * This must have enough capacity to store the result, or an
+ * error code will be returned.
+ * @param outBufCapacity the capacity of outBuf
+ * @param quality the jpeg-quality (1-100) to use
+ * @param cropLeft left-edge of the bounds of the image to crop to before
+ * rotation
+ * @param cropTop top-edge of the bounds of the image to crop to before
+ * rotation
+ * @param cropRight right-edge of the bounds of the image to crop to before
+ * rotation
+ * @param cropBottom bottom-edge of the bounds of the image to crop to
+ * before rotation
+ * @param rot90 the multiple of 90 to rotate the image CCW (after cropping)
+ */
+ private static native int compressJpegFromYUV420pNative(
+ int width, int height,
+ ByteBuffer yBuf, int yPStride, int yRStride,
+ ByteBuffer cbBuf, int cbPStride, int cbRStride,
+ ByteBuffer crBuf, int crPStride, int crRStride,
+ ByteBuffer outBuf, int outBufCapacity,
+ int quality,
+ int cropLeft, int cropTop, int cropRight, int cropBottom,
+ int rot90);
+
+ public void process(List<CaptureBundle> captureBundle) throws RemoteException {
+ JpegParameters jpegParams = getJpegParameters(captureBundle);
+ try {
+ mJpegParameters.add(jpegParams);
+ mProcessor.process(captureBundle);
+ } catch (Exception e) {
+ mJpegParameters.remove(jpegParams);
+ throw e;
+ }
+ }
+
+ public void onOutputSurface(Surface surface, int format) throws RemoteException {
+ if (format != ImageFormat.JPEG) {
+ Log.e(TAG, "Unsupported output format: " + format);
+ return;
+ }
+ mOutputSurface = surface;
+ initializePipeline();
+ }
+
+ @Override
+ public void onResolutionUpdate(android.hardware.camera2.extension.Size size)
+ throws RemoteException {
+ mResolution = size;
+ initializePipeline();
+ }
+
+ public void onImageFormatUpdate(int format) throws RemoteException {
+ if (format != ImageFormat.YUV_420_888) {
+ Log.e(TAG, "Unsupported input format: " + format);
+ return;
+ }
+ mFormat = format;
+ initializePipeline();
+ }
+
+ private void initializePipeline() throws RemoteException {
+ if ((mFormat != -1) && (mOutputSurface != null) && (mResolution != null) &&
+ (mYuvReader == null)) {
+ // Jpeg/blobs are expected to be configured with (w*h)x1
+ mOutputWriter = ImageWriter.newInstance(mOutputSurface, 1 /*maxImages*/,
+ ImageFormat.JPEG, mResolution.width * mResolution.height, 1);
+ mYuvReader = ImageReader.newInstance(mResolution.width, mResolution.height, mFormat,
+ JPEG_QUEUE_SIZE);
+ mYuvReader.setOnImageAvailableListener(new YuvCallback(), mHandler);
+ mProcessor.onOutputSurface(mYuvReader.getSurface(), mFormat);
+ mProcessor.onResolutionUpdate(mResolution);
+ mProcessor.onImageFormatUpdate(mFormat);
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ throw new UnsupportedOperationException("Binder IPC not supported!");
+ }
+
+ private class YuvCallback implements ImageReader.OnImageAvailableListener {
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ Image yuvImage = null;
+ Image jpegImage = null;
+ try {
+ yuvImage = mYuvReader.acquireNextImage();
+ jpegImage = mOutputWriter.dequeueInputImage();
+ } catch (IllegalStateException e) {
+ if (yuvImage != null) {
+ yuvImage.close();
+ }
+ if (jpegImage != null) {
+ jpegImage.close();
+ }
+ Log.e(TAG, "Failed to acquire processed yuv image or jpeg image!");
+ return;
+ }
+
+ ByteBuffer jpegBuffer = jpegImage.getPlanes()[0].getBuffer();
+ jpegBuffer.clear();
+ // Jpeg/blobs are expected to be configured with (w*h)x1
+ int jpegCapacity = jpegImage.getWidth();
+
+ Plane lumaPlane = yuvImage.getPlanes()[0];
+ Plane crPlane = yuvImage.getPlanes()[1];
+ Plane cbPlane = yuvImage.getPlanes()[2];
+
+ Iterator<JpegParameters> jpegIter = mJpegParameters.iterator();
+ JpegParameters jpegParams = null;
+ while(jpegIter.hasNext()) {
+ JpegParameters currentParams = jpegIter.next();
+ if (currentParams.mTimeStamps.contains(yuvImage.getTimestamp())) {
+ jpegParams = currentParams;
+ jpegIter.remove();
+ break;
+ }
+ }
+ if (jpegParams == null) {
+ if (mJpegParameters.isEmpty()) {
+ Log.w(TAG, "Empty jpeg settings queue! Using default jpeg orientation"
+ + " and quality!");
+ jpegParams = new JpegParameters();
+ jpegParams.mRotation = JPEG_DEFAULT_ROTATION;
+ jpegParams.mQuality = JPEG_DEFAULT_QUALITY;
+ } else {
+ Log.w(TAG, "No jpeg settings found with matching timestamp for current"
+ + " processed input!");
+ Log.w(TAG, "Using values from the top of the queue!");
+ jpegParams = mJpegParameters.poll();
+ }
+ }
+
+ compressJpegFromYUV420pNative(
+ yuvImage.getWidth(), yuvImage.getHeight(),
+ lumaPlane.getBuffer(), lumaPlane.getPixelStride(), lumaPlane.getRowStride(),
+ crPlane.getBuffer(), crPlane.getPixelStride(), crPlane.getRowStride(),
+ cbPlane.getBuffer(), cbPlane.getPixelStride(), cbPlane.getRowStride(),
+ jpegBuffer, jpegCapacity, jpegParams.mQuality,
+ 0, 0, yuvImage.getWidth(), yuvImage.getHeight(),
+ jpegParams.mRotation);
+ yuvImage.close();
+
+ try {
+ mOutputWriter.queueInputImage(jpegImage);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to queue encoded result!");
+ } finally {
+ jpegImage.close();
+ }
+ }
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 8451ded..0a56171 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -91,6 +91,7 @@
private ImageReader mStubCaptureImageReader = null;
private ImageWriter mRepeatingRequestImageWriter = null;
+ private CameraExtensionJpegProcessor mImageJpegProcessor = null;
private ICaptureProcessorImpl mImageProcessor = null;
private CameraExtensionForwardProcessor mPreviewImageProcessor = null;
private IRequestUpdateProcessorImpl mPreviewRequestUpdateProcessor = null;
@@ -413,6 +414,10 @@
if (mImageProcessor != null) {
if (mClientCaptureSurface != null) {
SurfaceInfo surfaceInfo = querySurface(mClientCaptureSurface);
+ if (surfaceInfo.mFormat == ImageFormat.JPEG) {
+ mImageJpegProcessor = new CameraExtensionJpegProcessor(mImageProcessor);
+ mImageProcessor = mImageJpegProcessor;
+ }
mBurstCaptureImageReader = ImageReader.newInstance(surfaceInfo.mWidth,
surfaceInfo.mHeight, CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
mImageExtender.getMaxCaptureStage());
@@ -570,14 +575,16 @@
return null;
}
- // Set user supported jpeg quality and rotation parameters
+ // This will override the extension capture stage jpeg parameters with the user set
+ // jpeg quality and rotation. This will guarantee that client configured jpeg
+ // parameters always have highest priority.
Integer jpegRotation = clientRequest.get(CaptureRequest.JPEG_ORIENTATION);
if (jpegRotation != null) {
- requestBuilder.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
+ captureStage.parameters.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
}
Byte jpegQuality = clientRequest.get(CaptureRequest.JPEG_QUALITY);
if (jpegQuality != null) {
- requestBuilder.set(CaptureRequest.JPEG_QUALITY, jpegQuality);
+ captureStage.parameters.set(CaptureRequest.JPEG_QUALITY, jpegQuality);
}
requestBuilder.addTarget(target);
@@ -753,6 +760,11 @@
mPreviewImageProcessor = null;
}
+ if (mImageJpegProcessor != null) {
+ mImageJpegProcessor.close();
+ mImageJpegProcessor = null;
+ }
+
mCaptureSession = null;
mImageProcessor = null;
mCameraRepeatingSurface = mClientRepeatingRequestSurface = null;
@@ -1014,7 +1026,10 @@
mCaptureRequestMap.clear();
mCapturePendingMap.clear();
boolean processStatus = true;
- List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap);
+ Byte jpegQuality = mClientRequest.get(CaptureRequest.JPEG_QUALITY);
+ Integer jpegOrientation = mClientRequest.get(CaptureRequest.JPEG_ORIENTATION);
+ List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap,
+ jpegOrientation, jpegQuality);
try {
mImageProcessor.process(captureList);
} catch (RemoteException e) {
@@ -1444,10 +1459,8 @@
}
for (int i = idx; i >= 0; i--) {
if (previewMap.valueAt(i).first != null) {
- Log.w(TAG, "Discard pending buffer with timestamp: " + previewMap.keyAt(i));
previewMap.valueAt(i).first.close();
} else {
- Log.w(TAG, "Discard pending result with timestamp: " + previewMap.keyAt(i));
if (mClientNotificationsEnabled && ((i != idx) || notifyCurrentIndex)) {
Log.w(TAG, "Preview frame drop with timestamp: " + previewMap.keyAt(i));
final long ident = Binder.clearCallingIdentity();
@@ -1639,7 +1652,8 @@
}
private static List<CaptureBundle> initializeParcelable(
- HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap) {
+ HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap, Integer jpegOrientation,
+ Byte jpegQuality) {
ArrayList<CaptureBundle> ret = new ArrayList<>();
for (Integer stagetId : captureMap.keySet()) {
Pair<Image, TotalCaptureResult> entry = captureMap.get(stagetId);
@@ -1648,6 +1662,12 @@
bundle.captureImage = initializeParcelImage(entry.first);
bundle.sequenceId = entry.second.getSequenceId();
bundle.captureResult = entry.second.getNativeMetadata();
+ if (jpegOrientation != null) {
+ bundle.captureResult.set(CaptureResult.JPEG_ORIENTATION, jpegOrientation);
+ }
+ if (jpegQuality != null) {
+ bundle.captureResult.set(CaptureResult.JPEG_QUALITY, jpegQuality);
+ }
ret.add(bundle);
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 48b05b7..2d58520 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -67,12 +67,6 @@
public abstract boolean isProximitySensorAvailable();
/**
- * Returns the id of the {@link com.android.server.display.DisplayGroup} to which the provided
- * display belongs.
- */
- public abstract int getDisplayGroupId(int displayId);
-
- /**
* Registers a display group listener which will be informed of the addition, removal, or change
* of display groups.
*
@@ -469,7 +463,7 @@
void onStateChanged();
void onProximityPositive();
void onProximityNegative();
- void onDisplayStateChange(int state); // one of the Display state constants
+ void onDisplayStateChange(boolean allInactive, boolean allOff);
void acquireSuspendBlocker();
void releaseSuspendBlocker();
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index a9bcdeff..f66ecdf 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -831,6 +831,9 @@
case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
return context.getString(
com.android.internal.R.string.face_error_security_update_required);
+ case BIOMETRIC_ERROR_RE_ENROLL:
+ return context.getString(
+ com.android.internal.R.string.face_recalibrate_notification_content);
case FACE_ERROR_VENDOR: {
String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.face_error_vendor);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index eaa38f3..4743fee 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -25,6 +25,8 @@
import android.os.CombinedVibrationEffect;
import android.hardware.input.IInputSensorEventListener;
import android.hardware.input.InputSensorInfo;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
import android.os.IBinder;
import android.os.IVibratorStateListener;
import android.os.VibrationEffect;
@@ -127,4 +129,14 @@
void disableSensor(int deviceId, int sensorType);
boolean flushSensor(int deviceId, int sensorType);
+
+ List<Light> getLights(int deviceId);
+
+ LightState getLightState(int deviceId, int lightId);
+
+ void setLightStates(int deviceId, in int[] lightIds, in LightState[] states, in IBinder token);
+
+ void openLightSession(int deviceId, String opPkg, in IBinder token);
+
+ void closeLightSession(int deviceId, in IBinder token);
}
diff --git a/core/java/android/hardware/input/InputDeviceLightsManager.java b/core/java/android/hardware/input/InputDeviceLightsManager.java
new file mode 100644
index 0000000..a3b91a9
--- /dev/null
+++ b/core/java/android/hardware/input/InputDeviceLightsManager.java
@@ -0,0 +1,139 @@
+/*
+ * 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.hardware.input;
+
+import android.annotation.NonNull;
+import android.app.ActivityThread;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
+import android.util.CloseGuard;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.ref.Reference;
+import java.util.List;
+
+/**
+ * LightsManager manages an input device's lights {@link android.hardware.input.Light}.
+ */
+class InputDeviceLightsManager extends LightsManager {
+ private static final String TAG = "InputDeviceLightsManager";
+ private static final boolean DEBUG = false;
+
+ private final InputManager mInputManager;
+
+ // The input device ID.
+ private final int mDeviceId;
+ // Package name
+ private final String mPackageName;
+
+ InputDeviceLightsManager(InputManager inputManager, int deviceId) {
+ super(ActivityThread.currentActivityThread().getSystemContext());
+ mInputManager = inputManager;
+ mDeviceId = deviceId;
+ mPackageName = ActivityThread.currentPackageName();
+ }
+
+ /**
+ * Returns the lights available on the device.
+ *
+ * @return A list of available lights
+ */
+ @Override
+ public @NonNull List<Light> getLights() {
+ return mInputManager.getLights(mDeviceId);
+ }
+
+ /**
+ * Returns the state of a specified light.
+ *
+ * @hide
+ */
+ @Override
+ public @NonNull LightState getLightState(@NonNull Light light) {
+ Preconditions.checkNotNull(light);
+ return mInputManager.getLightState(mDeviceId, light);
+ }
+
+ /**
+ * Creates a new LightsSession that can be used to control the device lights.
+ */
+ @Override
+ public @NonNull LightsSession openSession() {
+ final LightsSession session = new InputDeviceLightsSession();
+ mInputManager.openLightSession(mDeviceId, mPackageName, session.getToken());
+ return session;
+ }
+
+ /**
+ * Encapsulates a session that can be used to control device lights and represents the lifetime
+ * of the requests.
+ */
+ public final class InputDeviceLightsSession extends LightsManager.LightsSession
+ implements AutoCloseable {
+
+ private final CloseGuard mCloseGuard = new CloseGuard();
+ private boolean mClosed = false;
+
+ /**
+ * Instantiated by {@link LightsManager#openSession()}.
+ */
+ private InputDeviceLightsSession() {
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * Sends a request to modify the states of multiple lights.
+ *
+ * @param request the settings for lights that should change
+ */
+ @Override
+ public void requestLights(@NonNull LightsRequest request) {
+ Preconditions.checkNotNull(request);
+ Preconditions.checkArgument(!mClosed);
+
+ mInputManager.requestLights(mDeviceId, request, getToken());
+ }
+
+ /**
+ * Closes the session, reverting all changes made through it.
+ */
+ @Override
+ public void close() {
+ if (!mClosed) {
+ mInputManager.closeLightSession(mDeviceId, getToken());
+ mClosed = true;
+ mCloseGuard.close();
+ }
+ Reference.reachabilityFence(this);
+ }
+
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 8a01c66..e15d629 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -30,6 +30,10 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.hardware.SensorManager;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
import android.os.BlockUntrustedTouchesMode;
import android.os.Build;
import android.os.CombinedVibrationEffect;
@@ -1409,7 +1413,7 @@
}
/**
- * Gets a vibrator service associated with an input device, always create a new instance.
+ * Gets a vibrator service associated with an input device, always creates a new instance.
* @return The vibrator, never null.
* @hide
*/
@@ -1418,7 +1422,7 @@
}
/**
- * Gets a vibrator manager service associated with an input device, always create a new
+ * Gets a vibrator manager service associated with an input device, always creates a new
* instance.
* @return The vibrator manager, never null.
* @hide
@@ -1486,10 +1490,8 @@
/**
* Register input device vibrator state listener
- *
- * @hide
*/
- public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
try {
return mIm.registerVibratorStateListener(deviceId, listener);
} catch (RemoteException ex) {
@@ -1499,10 +1501,8 @@
/**
* Unregister input device vibrator state listener
- *
- * @hide
*/
- public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
try {
return mIm.unregisterVibratorStateListener(deviceId, listener);
} catch (RemoteException ex) {
@@ -1511,7 +1511,7 @@
}
/**
- * Gets a sensor manager service associated with an input device, always create a new instance.
+ * Gets a sensor manager service associated with an input device, always creates a new instance.
* @return The sensor manager, never null.
* @hide
*/
@@ -1533,6 +1533,86 @@
}
/**
+ * Gets a lights manager associated with an input device, always creates a new instance.
+ * @return The lights manager, never null.
+ * @hide
+ */
+ @NonNull
+ public LightsManager getInputDeviceLightsManager(int deviceId) {
+ return new InputDeviceLightsManager(InputManager.this, deviceId);
+ }
+
+ /**
+ * Gets a list of light objects associated with an input device.
+ * @return The list of lights, never null.
+ */
+ @NonNull List<Light> getLights(int deviceId) {
+ try {
+ return mIm.getLights(deviceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the state of an input device light.
+ * @return the light state
+ */
+ @NonNull LightState getLightState(int deviceId, @NonNull Light light) {
+ try {
+ return mIm.getLightState(deviceId, light.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to modify the states of multiple lights.
+ *
+ * @param request the settings for lights that should change
+ */
+ void requestLights(int deviceId, @NonNull LightsRequest request, IBinder token) {
+ try {
+ List<Integer> lightIdList = request.getLights();
+ int[] lightIds = new int[lightIdList.size()];
+ for (int i = 0; i < lightIds.length; i++) {
+ lightIds[i] = lightIdList.get(i);
+ }
+ List<LightState> lightStateList = request.getLightStates();
+ mIm.setLightStates(deviceId, lightIds,
+ lightStateList.toArray(new LightState[lightStateList.size()]),
+ token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Open light session for input device manager
+ *
+ * @param token The token for the light session
+ */
+ void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) {
+ try {
+ mIm.openLightSession(deviceId, opPkg, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Close light session
+ *
+ */
+ void closeLightSession(int deviceId, @NonNull IBinder token) {
+ try {
+ mIm.closeLightSession(deviceId, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Listens for changes in input devices.
*/
public interface InputDeviceListener {
diff --git a/core/java/android/hardware/input/OWNERS b/core/java/android/hardware/input/OWNERS
index 25e02e1..c390b33 100644
--- a/core/java/android/hardware/input/OWNERS
+++ b/core/java/android/hardware/input/OWNERS
@@ -1,6 +1,3 @@
# Bug component: 136048
include /services/core/java/com/android/server/input/OWNERS
-
-michaelwr@google.com
-svv@google.com
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index da27018..7bfff5d 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -16,22 +16,56 @@
package android.hardware.lights;
+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;
+
/**
* Represents a logical light on the device.
*
- * @hide
*/
-@SystemApi
public final class Light implements Parcelable {
+ // These enum values copy the values from {@link com.android.server.lights.LightsManager}
+ // and the light HAL. Since 0-7 are lights reserved for system use, 8 for microphone light is
+ // defined in {@link android.hardware.lights.LightsManager}, following types are available
+ // through this API.
+ /** Type for lights that indicate microphone usage */
+ public static final int LIGHT_TYPE_MICROPHONE = 8;
+
+ /**
+ * Type for lights that indicate a monochrome color LED light.
+ */
+ public static final int LIGHT_TYPE_INPUT_SINGLE = 9;
+
+ /**
+ * Type for lights that indicate a group of LED lights representing player ID.
+ */
+ public static final int LIGHT_TYPE_INPUT_PLAYER_ID = 10;
+
+ /**
+ * Type for lights that indicate a color LED light.
+ */
+ public static final int LIGHT_TYPE_INPUT_RGB = 11;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"LIGHT_TYPE_"},
+ value = {
+ LIGHT_TYPE_INPUT_PLAYER_ID,
+ LIGHT_TYPE_INPUT_SINGLE,
+ LIGHT_TYPE_INPUT_RGB,
+ })
+ public @interface LightType {}
+
private final int mId;
private final int mOrdinal;
private final int mType;
+ private final String mName;
/**
* Creates a new light with the given data.
@@ -39,15 +73,26 @@
* @hide
*/
public Light(int id, int ordinal, int type) {
+ this(id, ordinal, type, "Light");
+ }
+
+ /**
+ * Creates a new light with the given data.
+ *
+ * @hide
+ */
+ public Light(int id, int ordinal, int type, String name) {
mId = id;
mOrdinal = ordinal;
mType = type;
+ mName = name;
}
private Light(@NonNull Parcel in) {
mId = in.readInt();
mOrdinal = in.readInt();
mType = in.readInt();
+ mName = in.readString();
}
/** Implement the Parcelable interface */
@@ -56,6 +101,7 @@
dest.writeInt(mId);
dest.writeInt(mOrdinal);
dest.writeInt(mType);
+ dest.writeString(mName);
}
/** Implement the Parcelable interface */
@@ -100,6 +146,14 @@
}
/**
+ * Returns the name of the light.
+ */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /**
* Returns the ordinal of the light.
*
* <p>This is a sort key that represents the physical order of lights on the device with the
diff --git a/core/java/android/hardware/lights/LightState.java b/core/java/android/hardware/lights/LightState.java
index cd39e6d..650b383 100644
--- a/core/java/android/hardware/lights/LightState.java
+++ b/core/java/android/hardware/lights/LightState.java
@@ -32,36 +32,93 @@
* will be converted to only a brightness value and that will be used for the light's single
* channel.
*
- * @hide
*/
-@SystemApi
public final class LightState implements Parcelable {
private final int mColor;
+ private final int mPlayerId;
/**
- * Creates a new LightState with the desired color and intensity.
+ * Creates a new LightState with the desired color and intensity, for a light type
+ * of RBG color or monochrome color.
*
* @param color the desired color and intensity in ARGB format.
+ * @deprecated this has been replaced with {@link android.hardware.lights.LightState#forColor }
+ * @hide
*/
+ @Deprecated
+ @SystemApi
public LightState(@ColorInt int color) {
- mColor = color;
- }
-
- private LightState(@NonNull Parcel in) {
- mColor = in.readInt();
+ this(color, 0);
}
/**
- * Return the color and intensity associated with this LightState.
- * @return the color and intensity in ARGB format. The A channel is ignored.
+ * Creates a new LightState with the desired color and intensity, and the player Id.
+ * Player Id will only be applied on Light type
+ * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}
+ *
+ * @param color the desired color and intensity in ARGB format.
+ * @hide
+ */
+ public LightState(@ColorInt int color, int playerId) {
+ mColor = color;
+ mPlayerId = playerId;
+ }
+
+ /**
+ * Creates a new LightState with the desired color and intensity, for a light type
+ * of RBG color or single monochrome color.
+ *
+ * @param color the desired color and intensity in ARGB format.
+ * @return The LightState object contains the color.
+ */
+ @NonNull
+ public static LightState forColor(@ColorInt int color) {
+ return new LightState(color, 0);
+ }
+
+ /**
+ * Creates a new LightState with the desired player id, for a light of type
+ * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}.
+ *
+ * @param playerId the desired player id.
+ * @return The LightState object contains the player id.
+ */
+ @NonNull
+ public static LightState forPlayerId(int playerId) {
+ return new LightState(0, playerId);
+ }
+
+ /**
+ * Creates a new LightState from a parcel object.
+ */
+ private LightState(@NonNull Parcel in) {
+ mColor = in.readInt();
+ mPlayerId = in.readInt();
+ }
+
+ /**
+ * Returns the color and intensity associated with this LightState.
+ * @return the color and intensity in ARGB format. The A channel is ignored. return 0 when
+ * calling LightsManager.getLightState with LIGHT_TYPE_INPUT_PLAYER_ID.
*/
public @ColorInt int getColor() {
return mColor;
}
+ /**
+ * Returns the player ID associated with this LightState for Light type
+ * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID},
+ * or 0 for other types.
+ * @return the player ID.
+ */
+ public int getPlayerId() {
+ return mPlayerId;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mColor);
+ dest.writeInt(mPlayerId);
}
@Override
@@ -69,6 +126,12 @@
return 0;
}
+ @Override
+ public String toString() {
+ return "LightState{Color=0x" + Integer.toHexString(mColor) + ", PlayerId="
+ + mPlayerId + "}";
+ }
+
public static final @NonNull Parcelable.Creator<LightState> CREATOR =
new Parcelable.Creator<LightState>() {
public LightState createFromParcel(Parcel in) {
diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java
index 33e5fca..8fd56db 100644
--- a/core/java/android/hardware/lights/LightsManager.java
+++ b/core/java/android/hardware/lights/LightsManager.java
@@ -16,43 +16,38 @@
package android.hardware.lights;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.CloseGuard;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.Reference;
import java.util.List;
/**
* The LightsManager class allows control over device lights.
*
- * @hide
*/
-@SystemApi
@SystemService(Context.LIGHTS_SERVICE)
-public final class LightsManager {
+public abstract class LightsManager {
private static final String TAG = "LightsManager";
+ @NonNull private final Context mContext;
// These enum values copy the values from {@link com.android.server.lights.LightsManager}
// and the light HAL. Since 0-7 are lights reserved for system use, only the microphone light
- // is available through this API.
- /** Type for lights that indicate microphone usage */
+ // and following types are available through this API.
+ /** Type for lights that indicate microphone usage
+ * @deprecated this has been moved to {@link android.hardware.lights.Light }
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
public static final int LIGHT_TYPE_MICROPHONE = 8;
/** @hide */
@@ -63,28 +58,11 @@
})
public @interface LightType {}
- @NonNull private final Context mContext;
- @NonNull private final ILightsManager mService;
-
/**
- * Creates a LightsManager.
- *
- * @hide
+ * @hide to prevent subclassing from outside of the framework
*/
- public LightsManager(@NonNull Context context) throws ServiceNotFoundException {
- this(context, ILightsManager.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE)));
- }
-
- /**
- * Creates a LightsManager with a provided service implementation.
- *
- * @hide
- */
- @VisibleForTesting
- public LightsManager(@NonNull Context context, @NonNull ILightsManager service) {
+ public LightsManager(Context context) {
mContext = Preconditions.checkNotNull(context);
- mService = Preconditions.checkNotNull(service);
}
/**
@@ -92,112 +70,44 @@
*
* @return A list of available lights
*/
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- public @NonNull List<Light> getLights() {
- try {
- return mService.getLights();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ public @NonNull abstract List<Light> getLights();
/**
* Returns the state of a specified light.
*
- * @hide
*/
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- @TestApi
- public @NonNull LightState getLightState(@NonNull Light light) {
- Preconditions.checkNotNull(light);
- try {
- return mService.getLightState(light.getId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ public abstract @NonNull LightState getLightState(@NonNull Light light);
/**
* Creates a new LightsSession that can be used to control the device lights.
*/
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- public @NonNull LightsSession openSession() {
- try {
- final LightsSession session = new LightsSession();
- mService.openSession(session.mToken);
- return session;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ public abstract @NonNull LightsSession openSession();
/**
* Encapsulates a session that can be used to control device lights and represents the lifetime
* of the requests.
*/
- public final class LightsSession implements AutoCloseable {
-
+ public abstract static class LightsSession implements AutoCloseable {
private final IBinder mToken = new Binder();
-
- private final CloseGuard mCloseGuard = new CloseGuard();
- private boolean mClosed = false;
-
- /**
- * Instantiated by {@link LightsManager#openSession()}.
- */
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- private LightsSession() {
- mCloseGuard.open("close");
- }
-
/**
* Sends a request to modify the states of multiple lights.
*
- * <p>This method only controls lights that aren't overridden by higher-priority sessions.
- * Additionally, lights not controlled by this session can be controlled by lower-priority
- * sessions.
- *
* @param request the settings for lights that should change
*/
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- public void requestLights(@NonNull LightsRequest request) {
- Preconditions.checkNotNull(request);
- if (!mClosed) {
- try {
- mService.setLightStates(mToken, request.mLightIds, request.mLightStates);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
+ public abstract void requestLights(@NonNull LightsRequest request);
+
+ @Override
+ public abstract void close();
/**
- * Closes the session, reverting all changes made through it.
+ * Get the token of a light session.
+ *
+ * @return Binder token of the light session.
+ * @hide
*/
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- @Override
- public void close() {
- if (!mClosed) {
- try {
- mService.closeSession(mToken);
- mClosed = true;
- mCloseGuard.close();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- Reference.reachabilityFence(this);
- }
-
- /** @hide */
- @Override
- protected void finalize() throws Throwable {
- try {
- mCloseGuard.warnIfOpen();
- close();
- } finally {
- super.finalize();
- }
+ public @NonNull IBinder getToken() {
+ return mToken;
}
}
+
}
diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java
index a318992..2626a46 100644
--- a/core/java/android/hardware/lights/LightsRequest.java
+++ b/core/java/android/hardware/lights/LightsRequest.java
@@ -17,17 +17,17 @@
package android.hardware.lights;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.util.SparseArray;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
/**
* Encapsulates a request to modify the state of multiple lights.
*
- * @hide
*/
-@SystemApi
public final class LightsRequest {
/** Visible to {@link LightsManager.Session}. */
@@ -50,6 +50,30 @@
}
/**
+ * Get a list of Light as ids. The ids will returned in same order as the lights passed
+ * in Builder.
+ *
+ * @return List of light ids
+ */
+ public @NonNull List<Integer> getLights() {
+ List<Integer> lightList = new ArrayList<Integer>(mLightIds.length);
+ for (int i = 0; i < mLightIds.length; i++) {
+ lightList.add(mLightIds[i]);
+ }
+ return lightList;
+ }
+
+ /**
+ * Get a list of LightState. The states will be returned in same order as the light states
+ * passed in Builder.
+ *
+ * @return List of light states
+ */
+ public @NonNull List<LightState> getLightStates() {
+ return Arrays.asList(mLightStates);
+ }
+
+ /**
* Builder for creating device light change requests.
*/
public static final class Builder {
@@ -62,7 +86,7 @@
* @param light the light to modify
* @param state the desired color and intensity of the light
*/
- public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) {
+ public @NonNull Builder addLight(@NonNull Light light, @NonNull LightState state) {
Preconditions.checkNotNull(light);
Preconditions.checkNotNull(state);
mChanges.put(light.getId(), state);
diff --git a/core/java/android/hardware/lights/SystemLightsManager.java b/core/java/android/hardware/lights/SystemLightsManager.java
new file mode 100644
index 0000000..726a613
--- /dev/null
+++ b/core/java/android/hardware/lights/SystemLightsManager.java
@@ -0,0 +1,181 @@
+/*
+ * 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.hardware.lights;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.hardware.lights.LightsManager.LightsSession;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.CloseGuard;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.lang.ref.Reference;
+import java.util.List;
+
+/**
+ * The LightsManager class allows control over device lights.
+ *
+ * @hide
+ */
+public final class SystemLightsManager extends LightsManager {
+ private static final String TAG = "LightsManager";
+
+ @NonNull private final ILightsManager mService;
+
+ /**
+ * Creates a SystemLightsManager.
+ *
+ * @hide
+ */
+ public SystemLightsManager(@NonNull Context context) throws ServiceNotFoundException {
+ this(context, ILightsManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE)));
+ }
+
+ /**
+ * Creates a SystemLightsManager with a provided service implementation.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public SystemLightsManager(@NonNull Context context, @NonNull ILightsManager service) {
+ super(context);
+ mService = Preconditions.checkNotNull(service);
+ }
+
+ /**
+ * Returns the lights available on the device.
+ *
+ * @return A list of available lights
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public @NonNull List<Light> getLights() {
+ try {
+ return mService.getLights();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the state of a specified light.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public @NonNull LightState getLightState(@NonNull Light light) {
+ Preconditions.checkNotNull(light);
+ try {
+ return mService.getLightState(light.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a new LightsSession that can be used to control the device lights.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public @NonNull LightsSession openSession() {
+ try {
+ final LightsSession session = new SystemLightsSession();
+ mService.openSession(session.getToken());
+ return session;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Encapsulates a session that can be used to control device lights and represents the lifetime
+ * of the requests.
+ */
+ public final class SystemLightsSession extends LightsManager.LightsSession
+ implements AutoCloseable {
+
+ private final CloseGuard mCloseGuard = new CloseGuard();
+ private boolean mClosed = false;
+
+ /**
+ * Instantiated by {@link LightsManager#openSession()}.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ private SystemLightsSession() {
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * Sends a request to modify the states of multiple lights.
+ *
+ * <p>This method only controls lights that aren't overridden by higher-priority sessions.
+ * Additionally, lights not controlled by this session can be controlled by lower-priority
+ * sessions.
+ *
+ * @param request the settings for lights that should change
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public void requestLights(@NonNull LightsRequest request) {
+ Preconditions.checkNotNull(request);
+ if (!mClosed) {
+ try {
+ mService.setLightStates(getToken(), request.mLightIds, request.mLightStates);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Closes the session, reverting all changes made through it.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public void close() {
+ if (!mClosed) {
+ try {
+ mService.closeSession(getToken());
+ mClosed = true;
+ mCloseGuard.close();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ Reference.reachabilityFence(this);
+ }
+
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 5cfcd66..9198eb7 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -171,7 +171,7 @@
SomeArgs args = (SomeArgs) msg.obj;
try {
inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
- (IInputMethodPrivilegedOperations) args.arg2);
+ (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3);
} finally {
args.recycle();
}
@@ -280,9 +280,10 @@
@BinderThread
@Override
public void initializeInternal(IBinder token, int displayId,
- IInputMethodPrivilegedOperations privOps) {
+ IInputMethodPrivilegedOperations privOps, int configChanges) {
mCaller.executeOrSendMessage(
- mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps));
+ mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps,
+ configChanges));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7e2be01..03dd306 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -70,6 +70,7 @@
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -131,6 +132,7 @@
import android.window.WindowMetricsHelper;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
@@ -513,6 +515,8 @@
private boolean mIsAutomotive;
private Handler mHandler;
private boolean mImeSurfaceScheduledForRemoval;
+ private Configuration mLastKnownConfig;
+ private int mHandledConfigChanges;
/**
* An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
@@ -588,12 +592,14 @@
@MainThread
@Override
public final void initializeInternal(@NonNull IBinder token, int displayId,
- IInputMethodPrivilegedOperations privilegedOperations) {
+ IInputMethodPrivilegedOperations privilegedOperations,
+ int configChanges) {
if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
Log.w(TAG, "The token has already registered, ignore this initialization.");
return;
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
+ mHandledConfigChanges = configChanges;
mPrivOps.set(privilegedOperations);
InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
updateInputMethodDisplay(displayId);
@@ -821,6 +827,9 @@
setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
}
final boolean isVisible = isInputViewShown();
+ if (isVisible && getResources() != null) {
+ mLastKnownConfig = getResources().getConfiguration();
+ }
final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
resultReceiver.send(visibilityChanged
@@ -1428,10 +1437,37 @@
* state: {@link #onStartInput} if input is active, and
* {@link #onCreateInputView} and {@link #onStartInputView} and related
* appropriate functions if the UI is displayed.
+ * <p>Starting with {@link Build.VERSION_CODES#S}, IMEs can opt into handling configuration
+ * changes themselves instead of being restarted with
+ * {@link android.R.styleable#InputMethod_configChanges}.
*/
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- resetStateForNewConfiguration();
+ if (shouldImeRestartForConfig(newConfig)) {
+ resetStateForNewConfiguration();
+ }
+ }
+
+ /**
+ * @return {@code true} if {@link InputMethodService} needs to restart to handle
+ * .{@link #onConfigurationChanged(Configuration)}
+ */
+ @VisibleForTesting
+ boolean shouldImeRestartForConfig(@NonNull Configuration newConfig) {
+ if (mLastKnownConfig == null) {
+ return true;
+ }
+ // If the new config is the same as the config this Service is already running with,
+ // then don't bother calling resetStateForNewConfiguration.
+ int diff = mLastKnownConfig.diffPublicOnly(newConfig);
+ if (diff != 0) {
+ // remove attrs not-relevant to IME service.
+ diff &= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+ diff &= ActivityInfo.CONFIG_KEYBOARD;
+ diff &= ActivityInfo.CONFIG_NAVIGATION;
+ }
+ int unhandledDiff = (diff & ~mHandledConfigChanges);
+ return unhandledDiff != 0;
}
private void resetStateForNewConfiguration() {
@@ -3181,7 +3217,17 @@
requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
}
}
-
+
+ @VisibleForTesting
+ void setLastKnownConfig(@NonNull Configuration config) {
+ mLastKnownConfig = config;
+ }
+
+ @VisibleForTesting
+ void setHandledConfigChanges(int configChanges) {
+ mHandledConfigChanges = configChanges;
+ }
+
void startExtractingText(boolean inputChanged) {
final ExtractEditText eet = mExtractEditText;
if (eet != null && getCurrentInputStarted()
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 6353a25..6641206 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -16,14 +16,17 @@
package android.net;
+import static android.app.ActivityManager.procStateToString;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessCapability;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -617,8 +620,18 @@
* to access network when the device is idle or in battery saver mode. Otherwise, false.
* @hide
*/
- public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
- return procState <= FOREGROUND_THRESHOLD_STATE;
+ public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(@Nullable UidState uidState) {
+ if (uidState == null) {
+ return false;
+ }
+ return isProcStateAllowedWhileIdleOrPowerSaveMode(uidState.procState, uidState.capability);
+ }
+
+ /** @hide */
+ public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(
+ int procState, @ProcessCapability int capability) {
+ return procState <= FOREGROUND_THRESHOLD_STATE
+ || (capability & ActivityManager.PROCESS_CAPABILITY_NETWORK) != 0;
}
/**
@@ -626,11 +639,44 @@
* to access network when the device is in data saver mode. Otherwise, false.
* @hide
*/
+ public static boolean isProcStateAllowedWhileOnRestrictBackground(@Nullable UidState uidState) {
+ if (uidState == null) {
+ return false;
+ }
+ return isProcStateAllowedWhileOnRestrictBackground(uidState.procState);
+ }
+
+ /** @hide */
public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
+ // Data saver and bg policy restrictions will only take procstate into account.
return procState <= FOREGROUND_THRESHOLD_STATE;
}
/** @hide */
+ public static final class UidState {
+ public int uid;
+ public int procState;
+ public int capability;
+
+ public UidState(int uid, int procState, int capability) {
+ this.uid = uid;
+ this.procState = procState;
+ this.capability = capability;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("{procState=");
+ sb.append(procStateToString(procState));
+ sb.append(",cap=");
+ ActivityManager.printCapabilitiesSummary(sb, capability);
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ /** @hide */
@TestApi
@NonNull
public static String resolveNetworkId(@NonNull WifiConfiguration config) {
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 79f9e6e..dbb3127 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -15,9 +15,6 @@
*/
package android.net;
-import static android.Manifest.permission.NETWORK_STACK;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -26,8 +23,7 @@
import android.os.IBinder;
import android.os.ServiceManager;
-import java.util.ArrayList;
-import java.util.Arrays;
+import com.android.net.module.util.PermissionUtils;
/**
* Constants and utilities for client code communicating with the network stack service.
* @hide
@@ -79,9 +75,14 @@
* @param context {@link android.content.Context} for the process.
*
* @hide
+ *
+ * @deprecated Use {@link PermissionUtils#enforceNetworkStackPermission} instead.
+ *
+ * TODO: remove this method and let the users call to PermissionUtils directly.
*/
+ @Deprecated
public static void checkNetworkStackPermission(final @NonNull Context context) {
- checkNetworkStackPermissionOr(context);
+ PermissionUtils.enforceNetworkStackPermission(context);
}
/**
@@ -92,31 +93,14 @@
* @param otherPermissions The set of permissions that could be the candidate permissions , or
* empty string if none of other permissions needed.
* @hide
+ *
+ * @deprecated Use {@link PermissionUtils#enforceNetworkStackPermissionOr} instead.
+ *
+ * TODO: remove this method and let the users call to PermissionUtils directly.
*/
+ @Deprecated
public static void checkNetworkStackPermissionOr(final @NonNull Context context,
final @NonNull String... otherPermissions) {
- ArrayList<String> permissions = new ArrayList<String>(Arrays.asList(otherPermissions));
- permissions.add(NETWORK_STACK);
- permissions.add(PERMISSION_MAINLINE_NETWORK_STACK);
- enforceAnyPermissionOf(context, permissions.toArray(new String[0]));
+ PermissionUtils.enforceNetworkStackPermissionOr(context, otherPermissions);
}
-
- private static void enforceAnyPermissionOf(final @NonNull Context context,
- final @NonNull String... permissions) {
- if (!checkAnyPermissionOf(context, permissions)) {
- throw new SecurityException("Requires one of the following permissions: "
- + String.join(", ", permissions) + ".");
- }
- }
-
- private static boolean checkAnyPermissionOf(final @NonNull Context context,
- final @NonNull String... permissions) {
- for (String permission : permissions) {
- if (context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
- return true;
- }
- }
- return false;
- }
-
}
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
index 49047d3..8f6510e 100644
--- a/core/java/android/net/NetworkWatchlistManager.java
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -16,6 +16,8 @@
package android.net;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
@@ -29,6 +31,7 @@
* Class that manage network watchlist in system.
* @hide
*/
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@SystemService(Context.NETWORK_WATCHLIST_SERVICE)
public class NetworkWatchlistManager {
@@ -90,6 +93,7 @@
/**
* Get Network Watchlist config file hash.
*/
+ @Nullable
public byte[] getWatchlistConfigHash() {
try {
return mNetworkWatchlistManager.getWatchlistConfigHash();
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index b172ccc..f0e7da7 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -42,10 +42,6 @@
stop = stopUid;
}
- public static UidRange createForUser(int userId) {
- return new UidRange(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
- }
-
/** Creates a UidRange for the specified user. */
public static UidRange createForUser(UserHandle user) {
final UserHandle nextUser = UserHandle.of(user.getIdentifier() + 1);
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index e43b0b6..f90fbaf 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -596,7 +596,8 @@
}
}
}
- mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null));
+ mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null,
+ RouteInfo.RTN_UNICAST));
mConfig.updateAllowedFamilies(address);
return this;
}
diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl
index 555e9b5..d91cef5 100644
--- a/core/java/android/net/vcn/IVcnStatusCallback.aidl
+++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl
@@ -17,8 +17,9 @@
package android.net.vcn;
/** @hide */
-interface IVcnStatusCallback {
+oneway interface IVcnStatusCallback {
void onEnteredSafeMode();
+ void onVcnStatusChanged(int statusCode);
void onGatewayConnectionError(
in int[] gatewayNetworkCapabilities,
int errorCode,
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index aea0ea9..eb8c251 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.net.LinkProperties;
@@ -72,8 +73,7 @@
public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
- private static final Map<
- VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+ private static final Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
@NonNull private final Context mContext;
@@ -93,13 +93,13 @@
}
/**
- * Get all currently registered VcnUnderlyingNetworkPolicyListeners for testing purposes.
+ * Get all currently registered VcnNetworkPolicyListeners for testing purposes.
*
* @hide
*/
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
- public static Map<VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+ public static Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
getAllPolicyListeners() {
return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS);
}
@@ -161,22 +161,15 @@
}
}
- // TODO: make VcnUnderlyingNetworkPolicyListener @SystemApi
+ // TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using
+ // the new VcnNetworkPolicyListener API
/**
* VcnUnderlyingNetworkPolicyListener is the interface through which internal system components
* can register to receive updates for VCN-underlying Network policies from the System Server.
*
* @hide
*/
- public interface VcnUnderlyingNetworkPolicyListener {
- /**
- * Notifies the implementation that the VCN's underlying Network policy has changed.
- *
- * <p>After receiving this callback, implementations MUST poll VcnManager for the updated
- * VcnUnderlyingNetworkPolicy via VcnManager#getUnderlyingNetworkPolicy.
- */
- void onPolicyChanged();
- }
+ public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyListener {}
/**
* Add a listener for VCN-underlying network policy updates.
@@ -185,29 +178,14 @@
* Listener
* @param listener the VcnUnderlyingNetworkPolicyListener to be added
* @throws SecurityException if the caller does not have permission NETWORK_FACTORY
- * @throws IllegalArgumentException if the specified VcnUnderlyingNetworkPolicyListener is
- * already registered
+ * @throws IllegalStateException if the specified VcnUnderlyingNetworkPolicyListener is already
+ * registered
* @hide
*/
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
public void addVcnUnderlyingNetworkPolicyListener(
@NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) {
- requireNonNull(executor, "executor must not be null");
- requireNonNull(listener, "listener must not be null");
-
- VcnUnderlyingNetworkPolicyListenerBinder binder =
- new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener);
- if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) {
- throw new IllegalArgumentException(
- "Attempting to add a listener that is already in use");
- }
-
- try {
- mService.addVcnUnderlyingNetworkPolicyListener(binder);
- } catch (RemoteException e) {
- REGISTERED_POLICY_LISTENERS.remove(listener);
- throw e.rethrowFromSystemServer();
- }
+ addVcnNetworkPolicyListener(executor, listener);
}
/**
@@ -220,19 +198,7 @@
*/
public void removeVcnUnderlyingNetworkPolicyListener(
@NonNull VcnUnderlyingNetworkPolicyListener listener) {
- requireNonNull(listener, "listener must not be null");
-
- VcnUnderlyingNetworkPolicyListenerBinder binder =
- REGISTERED_POLICY_LISTENERS.remove(listener);
- if (binder == null) {
- return;
- }
-
- try {
- mService.removeVcnUnderlyingNetworkPolicyListener(binder);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ removeVcnNetworkPolicyListener(listener);
}
/**
@@ -266,6 +232,170 @@
}
}
+ /**
+ * VcnNetworkPolicyListener is the interface through which internal system components (e.g.
+ * Network Factories) can register to receive updates for VCN-underlying Network policies from
+ * the System Server.
+ *
+ * <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks
+ * should register a VcnNetworkPolicyListener. VcnManager will then use this listener to notify
+ * the registrant when VCN Network policies change. Upon receiving this signal, the listener
+ * must check {@link VcnManager} for the current Network policy result for each of its Networks
+ * via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface VcnNetworkPolicyListener {
+ /**
+ * Notifies the implementation that the VCN's underlying Network policy has changed.
+ *
+ * <p>After receiving this callback, implementations should get the current {@link
+ * VcnNetworkPolicyResult} via {@link #applyVcnNetworkPolicy(NetworkCapabilities,
+ * LinkProperties)}.
+ */
+ void onPolicyChanged();
+ }
+
+ /**
+ * Add a listener for VCN-underlying Network policy updates.
+ *
+ * <p>A {@link VcnNetworkPolicyListener} is eligible to begin receiving callbacks once it is
+ * registered. No callbacks are guaranteed upon registration.
+ *
+ * @param executor the Executor that will be used for invoking all calls to the specified
+ * Listener
+ * @param listener the VcnNetworkPolicyListener to be added
+ * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+ * @throws IllegalStateException if the specified VcnNetworkPolicyListener is already registered
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public void addVcnNetworkPolicyListener(
+ @NonNull Executor executor, @NonNull VcnNetworkPolicyListener listener) {
+ requireNonNull(executor, "executor must not be null");
+ requireNonNull(listener, "listener must not be null");
+
+ VcnUnderlyingNetworkPolicyListenerBinder binder =
+ new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener);
+ if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) {
+ throw new IllegalStateException("listener is already registered with VcnManager");
+ }
+
+ try {
+ mService.addVcnUnderlyingNetworkPolicyListener(binder);
+ } catch (RemoteException e) {
+ REGISTERED_POLICY_LISTENERS.remove(listener);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove the specified VcnNetworkPolicyListener from VcnManager.
+ *
+ * <p>If the specified listener is not currently registered, this is a no-op.
+ *
+ * @param listener the VcnNetworkPolicyListener that will be removed
+ * @hide
+ */
+ @SystemApi
+ public void removeVcnNetworkPolicyListener(@NonNull VcnNetworkPolicyListener listener) {
+ requireNonNull(listener, "listener must not be null");
+
+ VcnUnderlyingNetworkPolicyListenerBinder binder =
+ REGISTERED_POLICY_LISTENERS.remove(listener);
+ if (binder == null) {
+ return;
+ }
+
+ try {
+ mService.removeVcnUnderlyingNetworkPolicyListener(binder);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Applies the network policy for a {@link android.net.Network} with the given parameters.
+ *
+ * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy
+ * may have changed via {@link VcnNetworkPolicyListener#onPolicyChanged()}, a Network Provider
+ * MUST poll for the updated Network policy based on that Network's capabilities and properties.
+ *
+ * @param networkCapabilities the NetworkCapabilities to be used in determining the Network
+ * policy result for this Network.
+ * @param linkProperties the LinkProperties to be used in determining the Network policy result
+ * for this Network.
+ * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+ * @return the {@link VcnNetworkPolicyResult} to be used for this Network.
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public VcnNetworkPolicyResult applyVcnNetworkPolicy(
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties) {
+ requireNonNull(networkCapabilities, "networkCapabilities must not be null");
+ requireNonNull(linkProperties, "linkProperties must not be null");
+
+ final VcnUnderlyingNetworkPolicy policy =
+ getUnderlyingNetworkPolicy(networkCapabilities, linkProperties);
+ return new VcnNetworkPolicyResult(
+ policy.isTeardownRequested(), policy.getMergedNetworkCapabilities());
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ VCN_STATUS_CODE_NOT_CONFIGURED,
+ VCN_STATUS_CODE_INACTIVE,
+ VCN_STATUS_CODE_ACTIVE,
+ VCN_STATUS_CODE_SAFE_MODE
+ })
+ public @interface VcnStatusCode {}
+
+ /**
+ * Value indicating that the VCN for the subscription group is not configured, or that the
+ * callback is not privileged for the subscription group.
+ *
+ * @hide
+ */
+ public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0;
+
+ /**
+ * Value indicating that the VCN for the subscription group is inactive.
+ *
+ * <p>A VCN is inactive if a {@link VcnConfig} is present for the subscription group, but the
+ * provisioning package is not privileged.
+ *
+ * @hide
+ */
+ public static final int VCN_STATUS_CODE_INACTIVE = 1;
+
+ /**
+ * Value indicating that the VCN for the subscription group is active.
+ *
+ * <p>A VCN is active if a {@link VcnConfig} is present for the subscription, the provisioning
+ * package is privileged, and the VCN is not in Safe Mode. In other words, a VCN is considered
+ * active while it is connecting, fully connected, and disconnecting.
+ *
+ * @hide
+ */
+ public static final int VCN_STATUS_CODE_ACTIVE = 2;
+
+ /**
+ * Value indicating that the VCN for the subscription group is in Safe Mode.
+ *
+ * <p>A VCN will be put into Safe Mode if any of the gateway connections were unable to
+ * establish a connection within a system-determined timeout (while underlying networks were
+ * available).
+ *
+ * @hide
+ */
+ public static final int VCN_STATUS_CODE_SAFE_MODE = 3;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
@@ -323,8 +453,18 @@
*
* <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration
* via {@link #setVcnConfig(ParcelUuid, VcnConfig)}.
+ *
+ * @hide
*/
- public abstract void onEnteredSafeMode();
+ public void onEnteredSafeMode() {}
+
+ /**
+ * Invoked when status of the VCN for this callback's subscription group changes.
+ *
+ * @param statusCode the code for the status change encountered by this {@link
+ * VcnStatusCallback}'s subscription group.
+ */
+ public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode);
/**
* Invoked when a VCN Gateway Connection corresponding to this callback's subscription
@@ -356,6 +496,11 @@
* <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier
* privileges for the specified subscription at the time of invocation.
*
+ * <p>{@link VcnStatusCallback#onVcnStatusChanged(int)} will be invoked on registration with the
+ * current status for the specified subscription group's VCN. If the registrant is not
+ * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be
+ * returned.
+ *
* @param subscriptionGroup The subscription group to match for callbacks
* @param executor The {@link Executor} to be used for invoking callbacks
* @param callback The VcnStatusCallback to be registered
@@ -415,18 +560,17 @@
}
/**
- * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System
- * Server.
+ * Binder wrapper for added VcnNetworkPolicyListeners to receive signals from System Server.
*
* @hide
*/
private static class VcnUnderlyingNetworkPolicyListenerBinder
extends IVcnUnderlyingNetworkPolicyListener.Stub {
@NonNull private final Executor mExecutor;
- @NonNull private final VcnUnderlyingNetworkPolicyListener mListener;
+ @NonNull private final VcnNetworkPolicyListener mListener;
private VcnUnderlyingNetworkPolicyListenerBinder(
- Executor executor, VcnUnderlyingNetworkPolicyListener listener) {
+ Executor executor, VcnNetworkPolicyListener listener) {
mExecutor = executor;
mListener = listener;
}
@@ -460,6 +604,12 @@
() -> mExecutor.execute(() -> mCallback.onEnteredSafeMode()));
}
+ @Override
+ public void onVcnStatusChanged(@VcnStatusCode int statusCode) {
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode)));
+ }
+
// TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling'
@Override
public void onGatewayConnectionError(
diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl b/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl
new file mode 100644
index 0000000..3f13abe
--- /dev/null
+++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.vcn;
+
+/** @hide */
+parcelable VcnNetworkPolicyResult;
diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.java b/core/java/android/net/vcn/VcnNetworkPolicyResult.java
new file mode 100644
index 0000000..5e93820
--- /dev/null
+++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.java
@@ -0,0 +1,114 @@
+/*
+ * 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.vcn;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.NetworkCapabilities;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * VcnNetworkPolicyResult represents the Network policy result for a Network transport applying its
+ * VCN policy via {@link VcnManager#applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
+ *
+ * <p>Bearers that are bringing up networks capable of acting as a VCN's underlying network should
+ * query for Network policy results upon any capability changes (e.g. changing of TRUSTED bit), and
+ * when prompted by VcnManagementService via {@link VcnManager.VcnNetworkPolicyListener}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VcnNetworkPolicyResult implements Parcelable {
+ private final boolean mIsTearDownRequested;
+ private final NetworkCapabilities mNetworkCapabilities;
+
+ /**
+ * Constructs a VcnNetworkPolicyResult with the specified parameters.
+ *
+ * @hide
+ */
+ public VcnNetworkPolicyResult(
+ boolean isTearDownRequested, @NonNull NetworkCapabilities networkCapabilities) {
+ Objects.requireNonNull(networkCapabilities, "networkCapabilities must be non-null");
+
+ mIsTearDownRequested = isTearDownRequested;
+ mNetworkCapabilities = networkCapabilities;
+ }
+
+ /**
+ * Returns whether this VCN policy result requires that the underlying Network should be torn
+ * down.
+ *
+ * <p>Upon querying for the current Network policy result, the bearer must check this method,
+ * and MUST tear down the corresponding Network if it returns true.
+ */
+ public boolean isTeardownRequested() {
+ return mIsTearDownRequested;
+ }
+
+ /**
+ * Returns the NetworkCapabilities that the bearer should be using for the corresponding
+ * Network.
+ */
+ @NonNull
+ public NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIsTearDownRequested, mNetworkCapabilities);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof VcnNetworkPolicyResult)) return false;
+ final VcnNetworkPolicyResult that = (VcnNetworkPolicyResult) o;
+
+ return mIsTearDownRequested == that.mIsTearDownRequested
+ && mNetworkCapabilities.equals(that.mNetworkCapabilities);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBoolean(mIsTearDownRequested);
+ dest.writeParcelable(mNetworkCapabilities, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<VcnNetworkPolicyResult> CREATOR =
+ new Creator<VcnNetworkPolicyResult>() {
+ public VcnNetworkPolicyResult createFromParcel(Parcel in) {
+ return new VcnNetworkPolicyResult(in.readBoolean(), in.readParcelable(null));
+ }
+
+ public VcnNetworkPolicyResult[] newArray(int size) {
+ return new VcnNetworkPolicyResult[size];
+ }
+ };
+}
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
index dd7c86d8..b47d564 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
@@ -33,8 +33,7 @@
* @hide
*/
public final class VcnUnderlyingNetworkPolicy implements Parcelable {
- private final boolean mIsTearDownRequested;
- private final NetworkCapabilities mMergedNetworkCapabilities;
+ private final VcnNetworkPolicyResult mVcnNetworkPolicyResult;
/**
* Constructs a VcnUnderlyingNetworkPolicy with the specified parameters.
@@ -46,8 +45,13 @@
Objects.requireNonNull(
mergedNetworkCapabilities, "mergedNetworkCapabilities must be nonnull");
- mIsTearDownRequested = isTearDownRequested;
- mMergedNetworkCapabilities = mergedNetworkCapabilities;
+ mVcnNetworkPolicyResult =
+ new VcnNetworkPolicyResult(isTearDownRequested, mergedNetworkCapabilities);
+ }
+
+ private VcnUnderlyingNetworkPolicy(@NonNull VcnNetworkPolicyResult vcnNetworkPolicyResult) {
+ this.mVcnNetworkPolicyResult =
+ Objects.requireNonNull(vcnNetworkPolicyResult, "vcnNetworkPolicyResult");
}
/**
@@ -55,7 +59,7 @@
* be torn down.
*/
public boolean isTeardownRequested() {
- return mIsTearDownRequested;
+ return mVcnNetworkPolicyResult.isTeardownRequested();
}
/**
@@ -64,12 +68,12 @@
*/
@NonNull
public NetworkCapabilities getMergedNetworkCapabilities() {
- return mMergedNetworkCapabilities;
+ return mVcnNetworkPolicyResult.getNetworkCapabilities();
}
@Override
public int hashCode() {
- return Objects.hash(mIsTearDownRequested, mMergedNetworkCapabilities);
+ return Objects.hash(mVcnNetworkPolicyResult);
}
@Override
@@ -78,8 +82,7 @@
if (!(o instanceof VcnUnderlyingNetworkPolicy)) return false;
final VcnUnderlyingNetworkPolicy that = (VcnUnderlyingNetworkPolicy) o;
- return mIsTearDownRequested == that.mIsTearDownRequested
- && mMergedNetworkCapabilities.equals(that.mMergedNetworkCapabilities);
+ return mVcnNetworkPolicyResult.equals(that.mVcnNetworkPolicyResult);
}
/** {@inheritDoc} */
@@ -91,16 +94,14 @@
/** {@inheritDoc} */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeBoolean(mIsTearDownRequested);
- dest.writeParcelable(mMergedNetworkCapabilities, flags);
+ dest.writeParcelable(mVcnNetworkPolicyResult, flags);
}
/** Implement the Parcelable interface */
public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR =
new Creator<VcnUnderlyingNetworkPolicy>() {
public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) {
- return new VcnUnderlyingNetworkPolicy(
- in.readBoolean(), in.readParcelable(null));
+ return new VcnUnderlyingNetworkPolicy(in.readParcelable(null));
}
public VcnUnderlyingNetworkPolicy[] newArray(int size) {
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 305c686..a435ac1 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -25,6 +25,7 @@
import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.content.Context;
import android.util.Log;
@@ -41,7 +42,15 @@
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
-/** Class that provides a privileged API to capture and consume bugreports. */
+/**
+ * Class that provides a privileged API to capture and consume bugreports.
+ *
+ * <p>This class may only be used by apps that currently have carrier privileges (see {@link
+ * android.telephony.TelephonyManager#hasCarrierPrivileges}) on an active SIM or priv-apps
+ * explicitly allowed by the device manufacturer.
+ *
+ * <p>Only one bugreport can be generated by the system at a time.
+ */
@SystemService(Context.BUGREPORT_SERVICE)
public final class BugreportManager {
@@ -56,7 +65,12 @@
mBinder = binder;
}
- /** An interface describing the callback for bugreport progress and status. */
+ /**
+ * An interface describing the callback for bugreport progress and status.
+ *
+ * <p>In general, callers can expect to receive {@link #onProgress} calls as the bugreport
+ * progresses, followed by a terminal call to either {@link #onFinished} or {@link #onError}.
+ */
public abstract static class BugreportCallback {
/**
* Possible error codes taking a bugreport can encounter.
@@ -75,15 +89,18 @@
})
public @interface BugreportErrorCode {}
- /** The input options were invalid */
+ /**
+ * The input options were invalid. For example, the destination file the app provided could
+ * not be written by the system.
+ */
public static final int BUGREPORT_ERROR_INVALID_INPUT =
IDumpstateListener.BUGREPORT_ERROR_INVALID_INPUT;
- /** A runtime error occurred */
+ /** A runtime error occurred. */
public static final int BUGREPORT_ERROR_RUNTIME =
IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR;
- /** User denied consent to share the bugreport */
+ /** User denied consent to share the bugreport. */
public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT =
IDumpstateListener.BUGREPORT_ERROR_USER_DENIED_CONSENT;
@@ -149,6 +166,7 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.DUMP)
+ @WorkerThread
public void startBugreport(
@NonNull ParcelFileDescriptor bugreportFd,
@Nullable ParcelFileDescriptor screenshotFd,
@@ -222,6 +240,7 @@
* @param callback callback for progress and status updates.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @WorkerThread
public void startConnectivityBugreport(
@NonNull ParcelFileDescriptor bugreportFd,
@NonNull @CallbackExecutor Executor executor,
@@ -247,6 +266,7 @@
* @throws SecurityException if trying to cancel another app's bugreport in progress
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @WorkerThread
public void cancelBugreport() {
try {
mBinder.cancelBugreport(-1 /* callingUid */, mContext.getOpPackageName());
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index ff9b4f4..e5163d8 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1881,6 +1881,10 @@
* Returns the current battery saver control mode. Values it may return are defined in
* AutoPowerSaveModeTriggers. Note that this is a global device state, not a per user setting.
*
+ * <p>Note: Prior to Android version {@link Build.VERSION_CODES#S}, any app calling this method
+ * was required to hold the {@link android.Manifest.permission#POWER_SAVER} permission. Starting
+ * from Android version {@link Build.VERSION_CODES#S}, that permission is no longer required.
+ *
* @return The current value power saver mode for the system.
*
* @see AutoPowerSaveModeTriggers
@@ -1889,7 +1893,6 @@
*/
@AutoPowerSaveModeTriggers
@SystemApi
- @RequiresPermission(android.Manifest.permission.POWER_SAVER)
public int getPowerSaveModeTrigger() {
try {
return mService.getPowerSaveModeTrigger();
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 54d2df8..136dc38 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -207,6 +207,12 @@
public static final int SE_UID = 1068;
/**
+ * Defines the UID/GID for the iorapd.
+ * @hide
+ */
+ public static final int IORAPD_UID = 1071;
+
+ /**
* Defines the UID/GID for the NetworkStack app.
* @hide
*/
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 93c1690..43184ea 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -26,6 +26,7 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
@@ -631,10 +632,15 @@
/**
* Prepare to apply an unattended update by asking the user for their Lock Screen Knowledge
* Factor (LSKF). If supplied, the {@code intentSender} will be called when the system is setup
- * and ready to apply the OTA. This API is expected to handle requests from multiple clients
- * simultaneously, e.g. from ota and mainline.
+ * and ready to apply the OTA. <p>
*
- * <p> The behavior of multi-client Resume on Reboot works as follows
+ * <p> If the device doesn't setup a lock screen, i.e. by checking
+ * {@link KeyguardManager#isKeyguardSecure()}, this API call will fail and throw an exception.
+ * Callers are expected to use {@link PowerManager#reboot(String)} directly without going
+ * through the RoR flow. <p>
+ *
+ * <p> This API is expected to handle requests from multiple clients simultaneously, e.g.
+ * from ota and mainline. The behavior of multi-client Resume on Reboot works as follows
* <li> Each client should call this function to prepare for Resume on Reboot before calling
* {@link #rebootAndApply(Context, String, boolean)} </li>
* <li> One client cannot clear the Resume on Reboot preparation of another client. </li>
@@ -658,6 +664,13 @@
if (updateToken == null) {
throw new NullPointerException("updateToken == null");
}
+
+ KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+ if (keyguardManager == null || !keyguardManager.isDeviceSecure()) {
+ throw new IOException("Failed to request LSKF because the device doesn't have a"
+ + " lock screen. ");
+ }
+
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
if (!rs.requestLskf(context.getPackageName(), intentSender)) {
throw new IOException("preparation for update failed");
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index d6fa733..b003d23 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -186,7 +186,8 @@
/**
* Return the ID of this vibrator.
*
- * @return The id of the vibrator controlled by this service.
+ * @return A non-negative integer representing the id of the vibrator controlled by this
+ * service, or -1 this service is not attached to any physical vibrator.
*/
public int getId() {
return -1;
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index 5dd38b6..5a01814 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -91,10 +91,10 @@
*
* <p>
* Pass in a {@link CombinedVibrationEffect} representing a combination of {@link
- * VibrationEffect} to be played on one or more vibrators.
+ * VibrationEffect VibrationEffects} to be played on one or more vibrators.
* </p>
*
- * @param effect an array of longs of times for which to turn the vibrator on or off.
+ * @param effect a combination of effects to be performed by one or more vibrators.
*/
@RequiresPermission(android.Manifest.permission.VIBRATE)
public final void vibrate(@NonNull CombinedVibrationEffect effect) {
@@ -109,7 +109,7 @@
* VibrationEffect} to be played on one or more vibrators.
* </p>
*
- * @param effect an array of longs of times for which to turn the vibrator on or off.
+ * @param effect a combination of effects to be performed by one or more vibrators.
* @param attributes {@link VibrationAttributes} corresponding to the vibration. For example,
* specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or
* {@link VibrationAttributes#USAGE_RINGTONE} for vibrations associated with
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index a078e04..73520e0 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -59,7 +59,6 @@
/**
* Set up files and directories used in an installation session. Only used by Incremental.
* All the files will be created in defaultStorage.
- * TODO(b/133435829): code clean up
*
* @throws IllegalStateException the session is not an Incremental installation session.
* @throws IOException if fails to setup files or directories.
@@ -73,12 +72,10 @@
@Nullable IStorageHealthListener healthListener,
@NonNull List<InstallationFileParcel> addedFiles,
@NonNull PerUidReadTimeouts[] perUidReadTimeouts,
- IPackageLoadingProgressCallback progressCallback) throws IOException {
- // TODO(b/136132412): validity check if session should not be incremental
+ @Nullable IPackageLoadingProgressCallback progressCallback) throws IOException {
IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
Context.INCREMENTAL_SERVICE);
if (incrementalManager == null) {
- // TODO(b/146080380): add incremental-specific error code
throw new IOException("Failed to obtain incrementalManager.");
}
@@ -89,7 +86,6 @@
try {
result.addApkFile(file);
} catch (IOException e) {
- // TODO(b/146080380): add incremental-specific error code
throw new IOException(
"Failed to add file to IncFS: " + file.name + ", reason: ", e);
}
@@ -203,7 +199,6 @@
/**
* Resets the states and unbinds storage instances for an installation session.
- * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
*/
public void cleanUp() {
if (mDefaultStorage == null) {
@@ -211,8 +206,8 @@
}
try {
+ mIncrementalManager.unregisterLoadingProgressCallbacks(mStageDir.getAbsolutePath());
mDefaultStorage.unBind(mStageDir.getAbsolutePath());
- mDefaultStorage.unregisterLoadingProgressListener();
} catch (IOException ignored) {
}
mDefaultStorage = null;
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 0589994..cec6a1f 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -342,7 +342,6 @@
storage.unregisterLoadingProgressListener();
}
- // TODO(b/165841827): handle reboot and app update
public boolean registerCallback(@NonNull IncrementalStorage storage,
@NonNull IPackageLoadingProgressCallback callback) {
final int storageId = storage.getId();
@@ -364,30 +363,6 @@
return storage.registerLoadingProgressListener(this);
}
- public boolean unregisterCallback(@NonNull IncrementalStorage storage,
- @NonNull IPackageLoadingProgressCallback callback) {
- final int storageId = storage.getId();
- final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
- synchronized (mCallbacks) {
- callbacksForStorage = mCallbacks.get(storageId);
- if (callbacksForStorage == null) {
- // no callback has ever been registered on this storage
- return false;
- }
- if (!callbacksForStorage.unregister(callback)) {
- // the callback was not registered
- return false;
- }
- if (callbacksForStorage.getRegisteredCallbackCount() > 0) {
- // other callbacks are still listening on this storage
- return true;
- }
- mCallbacks.delete(storageId);
- }
- // stop listening for this storage
- return storage.unregisterLoadingProgressListener();
- }
-
@Override
public void onStorageLoadingProgressChanged(int storageId, float progress) {
final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index a5d3c2a..3a5426c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1536,6 +1536,7 @@
}
/** {@hide} */
+ @TestApi
public static boolean isUserKeyUnlocked(int userId) {
if (sStorageManager == null) {
sStorageManager = IStorageManager.Stub
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index e134c29..4354920 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -275,6 +275,14 @@
public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot";
/**
+ * Namespace for features related to Reboot Readiness detection.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_REBOOT_READINESS = "reboot_readiness";
+
+ /**
* Namespace for Rollback flags that are applied immediately.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e979e13..345cc84 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -417,6 +417,22 @@
"android.settings.MANAGE_UNKNOWN_APP_SOURCES";
/**
+ * Activity Action: Show settings to allow configuration of
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission
+ *
+ * Input: Optionally, the Intent's data URI can specify the application package name to
+ * directly invoke the management GUI specific to the package name. For example
+ * "package:com.my.app".
+ * <p>
+ * Output: When a package data uri is passed as input, the activity result is set to
+ * {@link android.app.Activity#RESULT_OK} if the permission was granted to the app. Otherwise,
+ * the result is set to {@link android.app.Activity#RESULT_CANCELED}.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM =
+ "android.settings.REQUEST_SCHEDULE_EXACT_ALARM";
+
+ /**
* Activity Action: Show settings to allow configuration of cross-profile access for apps
*
* Input: Optionally, the Intent's data URI can specify the application package name to
@@ -10697,14 +10713,6 @@
"hdmi_cec_switch_enabled";
/**
- * HDMI CEC version to use. Defaults to v1.4b.
- * @hide
- */
- @Readable
- public static final String HDMI_CEC_VERSION =
- "hdmi_cec_version";
-
- /**
* Whether TV will automatically turn on upon reception of the CEC command
* <Text View On> or <Image View On>. (0 = false, 1 = true)
*
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
index 074d5f1..030b863 100644
--- a/core/java/android/provider/SimPhonebookContract.java
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -44,8 +44,11 @@
* The contract between the provider of contact records on the device's SIM cards and applications.
* Contains definitions of the supported URIs and columns.
*
- * <p>This content provider does not support any of the QUERY_ARG_SQL* bundle arguments. An
- * IllegalArgumentException will be thrown if these are included.
+ * <h3>Permissions</h3>
+ * <p>
+ * Querying this provider requires {@link android.Manifest.permission#READ_CONTACTS} and writing
+ * to this provider requires {@link android.Manifest.permission#WRITE_CONTACTS}
+ * </p>
*/
public final class SimPhonebookContract {
@@ -85,7 +88,73 @@
}
}
- /** Constants for the contact records on a SIM card. */
+ /**
+ * Constants for the contact records on a SIM card.
+ *
+ * <h3 id="simrecords-data">Data</h3>
+ * <p>
+ * Data is stored in a specific elementary file on a specific SIM card and these are isolated
+ * from each other. SIM cards are identified by their subscription ID. SIM cards may not support
+ * all or even any of the elementary file types. A SIM will have constraints on
+ * the values of the data that can be stored in each elementary file. The available SIMs,
+ * their supported elementary file types and the constraints on the data can be discovered by
+ * querying {@link ElementaryFiles#CONTENT_URI}. Each elementary file has a fixed capacity
+ * for the number of records that may be stored. This can be determined from the value
+ * of the {@link ElementaryFiles#MAX_RECORDS} column.
+ * </p>
+ * <p>
+ * The {@link SimRecords#PHONE_NUMBER} column can only contain dialable characters and this
+ * applies regardless of the SIM that is being used. See
+ * {@link android.telephony.PhoneNumberUtils#isDialable(char)} for more details. Additionally
+ * the phone number can contain at most {@link ElementaryFiles#PHONE_NUMBER_MAX_LENGTH}
+ * characters. The {@link SimRecords#NAME} column can contain at most
+ * {@link ElementaryFiles#NAME_MAX_LENGTH} bytes when it is encoded for storage on the SIM.
+ * Encoding is done internally and so the name should be provided unencoded but the number of
+ * bytes required to encode it will vary depending on the characters it contains. This length
+ * can be determined by calling
+ * {@link SimRecords#getEncodedNameLength(ContentResolver, String)}.
+ * </p>
+ * <h3>Operations </h3>
+ * <dl>
+ * <dd><b>Insert</b></dd>
+ * <p>
+ * Only {@link ElementaryFiles#EF_ADN} supports inserts. {@link SimRecords#PHONE_NUMBER}
+ * is a required column. If the value provided for this column is missing, null, empty
+ * or violates the requirements discussed in the <a href="#simrecords-data">Data</a>
+ * section above an {@link IllegalArgumentException} will be thrown. The
+ * {@link SimRecords#NAME} column may be omitted but if provided and it violates any of
+ * the requirements discussed in the <a href="#simrecords-data">Data</a> section above
+ * an {@link IllegalArgumentException} will be thrown.
+ * </p>
+ * <p>
+ * If an insert is not possible because the elementary file is full then an
+ * {@link IllegalStateException} will be thrown.
+ * </p>
+ * <dd><b>Update</b></dd>
+ * <p>
+ * Updates can only be performed for individual records on {@link ElementaryFiles#EF_ADN}.
+ * A specific record is referenced via the Uri returned by
+ * {@link SimRecords#getItemUri(int, int, int)}. Updates have the same constraints and
+ * behavior for the {@link SimRecords#PHONE_NUMBER} and {@link SimRecords#NAME} as insert.
+ * However, in the case of update the {@link SimRecords#PHONE_NUMBER} may be omitted as
+ * the existing record will already have a valid value.
+ * </p>
+ * <dd><b>Delete</b></dd>
+ * <p>
+ * Delete may only be performed for individual records on {@link ElementaryFiles#EF_ADN}.
+ * Deleting records will free up space for use by future inserts.
+ * </p>
+ * <dd><b>Query</b></dd>
+ * <p>
+ * All the records stored on a specific elementary file can be read via a Uri returned by
+ * {@link SimRecords#getContentUri(int, int)}. This query always returns all records; there
+ * is no support for filtering via a selection. An individual record can be queried via a Uri
+ * returned by {@link SimRecords#getItemUri(int, int, int)}. Queries will throw an
+ * {@link IllegalArgumentException} when the SIM with the subscription ID or the elementary file
+ * type are invalid or unavailable.
+ * </p>
+ * </dl>
+ */
public static final class SimRecords {
/**
@@ -197,8 +266,8 @@
* be discovered by querying {@link ElementaryFiles#CONTENT_URI}.
*
* <p>If a SIM with the provided subscription ID does not exist or the SIM with the provided
- * subscription ID doesn't support the specified entity file then queries will return
- * and empty cursor and inserts will throw an {@link IllegalArgumentException}
+ * subscription ID doesn't support the specified entity file then all operations will
+ * throw an {@link IllegalArgumentException}.
*
* @param subscriptionId the subscriptionId of the SIM card that this Uri will reference
* @param efType the elementary file on the SIM that this Uri will reference
@@ -233,6 +302,9 @@
* must be greater than 0. If there is no record with this record
* number in the specified entity file then it will be treated as a
* non-existent record.
+ * @see ElementaryFiles#SUBSCRIPTION_ID
+ * @see ElementaryFiles#EF_TYPE
+ * @see #RECORD_NUMBER
*/
@NonNull
public static Uri getItemUri(
@@ -287,7 +359,28 @@
}
- /** Constants for metadata about the elementary files of the SIM cards in the phone. */
+ /**
+ * Constants for metadata about the elementary files of the SIM cards in the phone.
+ *
+ * <h3>Operations </h3>
+ * <dl>
+ * <dd><b>Insert</b></dd>
+ * <p>Insert is not supported for the Uris defined in this class.</p>
+ * <dd><b>Update</b></dd>
+ * <p>Update is not supported for the Uris defined in this class.</p>
+ * <dd><b>Delete</b></dd>
+ * <p>Delete is not supported for the Uris defined in this class.</p>
+ * <dd><b>Query</b></dd>
+ * <p>
+ * The elementary files for all the inserted SIMs can be read via
+ * {@link ElementaryFiles#CONTENT_URI}. Unsupported elementary files are omitted from the
+ * results. This Uri always returns all supported elementary files for all available SIMs; it
+ * does not support filtering via a selection. A specific elementary file can be queried
+ * via a Uri returned by {@link ElementaryFiles#getItemUri(int, int)}. If the elementary file
+ * referenced by this Uri is unsupported by the SIM then the query will return an empty cursor.
+ * </p>
+ * </dl>
+ */
public static final class ElementaryFiles {
/** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */
diff --git a/core/java/android/uwb/AngleOfArrivalSupport.aidl b/core/java/android/uwb/AngleOfArrivalSupport.aidl
deleted file mode 100644
index 57666ff..0000000
--- a/core/java/android/uwb/AngleOfArrivalSupport.aidl
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-/**
- * @hide
- */
-@Backing(type="int")
-enum AngleOfArrivalSupport {
- /**
- * The device does not support angle of arrival
- */
- NONE,
-
- /**
- * The device supports planar angle of arrival
- */
- TWO_DIMENSIONAL,
-
- /**
- * The device does supports three dimensional angle of arrival with hemispherical azimuth angles
- */
- THREE_DIMENSIONAL_HEMISPHERICAL,
-
- /**
- * The device does supports three dimensional angle of arrival with full azimuth angles
- */
- THREE_DIMENSIONAL_SPHERICAL,
-}
-
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index b9c5508..468a69c 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -17,7 +17,6 @@
package android.uwb;
import android.os.PersistableBundle;
-import android.uwb.AngleOfArrivalSupport;
import android.uwb.IUwbAdapterStateCallbacks;
import android.uwb.IUwbRangingCallbacks;
import android.uwb.SessionHandle;
@@ -47,43 +46,6 @@
void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks);
/**
- * Returns true if ranging is supported, false otherwise
- */
- boolean isRangingSupported();
-
- /**
- * Get the angle of arrival supported by this device
- *
- * @return the angle of arrival type supported
- */
- AngleOfArrivalSupport getAngleOfArrivalSupport();
-
- /**
- * Generates a list of the supported 802.15.4z channels
- *
- * The list must be prioritized in the order of preferred channel usage.
- *
- * The list must only contain channels that are permitted to be used in the
- * device's current location.
- *
- * @return an array of support channels on the device for the current location.
- */
- int[] getSupportedChannels();
-
- /**
- * Generates a list of the supported 802.15.4z preamble codes
- *
- * The list must be prioritized in the order of preferred preamble usage.
- *
- * The list must only contain preambles that are permitted to be used in the
- * device's current location.
- *
- * @return an array of supported preambles on the device for the current
- * location.
- */
- int[] getSupportedPreambleCodes();
-
- /**
* Get the accuracy of the ranging timestamps
*
* @return accuracy of the ranging timestamps in nanoseconds
@@ -91,27 +53,6 @@
long getTimestampResolutionNanos();
/**
- * Get the supported number of simultaneous ranging sessions
- *
- * @return the supported number of simultaneous ranging sessions
- */
- int getMaxSimultaneousSessions();
-
- /**
- * Get the maximum number of remote devices per session when local device is initiator
- *
- * @return the maximum number of remote devices supported in a single session
- */
- int getMaxRemoteDevicesPerInitiatorSession();
-
- /**
- * Get the maximum number of remote devices per session when local device is responder
- *
- * @return the maximum number of remote devices supported in a single session
- */
- int getMaxRemoteDevicesPerResponderSession();
-
- /**
* Provides the capabilities and features of the device
*
* @return specification specific capabilities and features of the device
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 2dc0ba0..63a6d05 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -32,10 +32,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -195,133 +191,6 @@
}
/**
- * Check if ranging is supported, regardless of ranging method
- *
- * @return true if ranging is supported
- */
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public boolean isRangingSupported() {
- try {
- return mUwbAdapter.isRangingSupported();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE,
- ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D,
- ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL,
- ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL})
- public @interface AngleOfArrivalSupportType {}
-
- /**
- * Indicate absence of support for angle of arrival measurement
- */
- public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1;
-
- /**
- * Indicate support for planar angle of arrival measurement, due to antenna
- * limitation. Typically requires at least two antennas.
- */
- public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2;
-
- /**
- * Indicate support for three dimensional angle of arrival measurement.
- * Typically requires at least three antennas. However, due to antenna
- * arrangement, a platform may only support hemi-spherical azimuth angles
- * ranging from -pi/2 to pi/2
- */
- public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3;
-
- /**
- * Indicate support for three dimensional angle of arrival measurement.
- * Typically requires at least three antennas. This mode supports full
- * azimuth angles ranging from -pi to pi.
- */
- public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4;
-
- /**
- * Gets the {@link AngleOfArrivalSupportType} supported on this platform
- * <p>Possible return values are
- * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE},
- * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D},
- * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL},
- * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL}.
- *
- * @return angle of arrival type supported
- */
- @AngleOfArrivalSupportType
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public int getAngleOfArrivalSupport() {
- try {
- switch (mUwbAdapter.getAngleOfArrivalSupport()) {
- case AngleOfArrivalSupport.TWO_DIMENSIONAL:
- return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D;
-
- case AngleOfArrivalSupport.THREE_DIMENSIONAL_HEMISPHERICAL:
- return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL;
-
- case AngleOfArrivalSupport.THREE_DIMENSIONAL_SPHERICAL:
- return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL;
-
- case AngleOfArrivalSupport.NONE:
- default:
- return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE;
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get a {@link List} of supported channel numbers based on the device's current location
- * <p>The returned values are ordered by the system's desired ordered of use, with the first
- * entry being the most preferred.
- *
- * <p>Channel numbers are defined based on the IEEE 802.15.4z standard for UWB.
- *
- * @return {@link List} of supported channel numbers ordered by preference
- */
- @NonNull
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public List<Integer> getSupportedChannelNumbers() {
- List<Integer> channels = new ArrayList<>();
- try {
- for (int channel : mUwbAdapter.getSupportedChannels()) {
- channels.add(channel);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- return channels;
- }
-
- /**
- * Get a {@link List} of supported preamble code indices
- * <p> Preamble code indices are defined based on the IEEE 802.15.4z standard for UWB.
- *
- * @return {@link List} of supported preamble code indices
- */
- @NonNull
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public Set<Integer> getSupportedPreambleCodeIndices() {
- Set<Integer> preambles = new HashSet<>();
- try {
- for (int preamble : mUwbAdapter.getSupportedPreambleCodes()) {
- preambles.add(preamble);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- return preambles;
- }
-
- /**
* Get the timestamp resolution for events in nanoseconds
* <p>This value defines the maximum error of all timestamps for events reported to
* {@link RangingSession.Callback}.
@@ -339,50 +208,6 @@
}
/**
- * Get the number of simultaneous sessions allowed in the system
- *
- * @return the maximum allowed number of simultaneously open {@link RangingSession} instances.
- */
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public int getMaxSimultaneousSessions() {
- try {
- return mUwbAdapter.getMaxSimultaneousSessions();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get the maximum number of remote devices in a {@link RangingSession} when the local device
- * is the initiator.
- *
- * @return the maximum number of remote devices per {@link RangingSession}
- */
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public int getMaxRemoteDevicesPerInitiatorSession() {
- try {
- return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get the maximum number of remote devices in a {@link RangingSession} when the local device
- * is a responder.
- *
- * @return the maximum number of remote devices per {@link RangingSession}
- */
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public int getMaxRemoteDevicesPerResponderSession() {
- try {
- return mUwbAdapter.getMaxRemoteDevicesPerResponderSession();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Open a {@link RangingSession} with the given parameters
* <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a
* {@link RangingSession} object used to control ranging when the session is successfully
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 6543de1..eb49e52 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -213,8 +213,7 @@
Index.FRAME_TIMELINE_VSYNC_ID,
Index.INTENDED_VSYNC,
Index.VSYNC,
- Index.OLDEST_INPUT_EVENT,
- Index.NEWEST_INPUT_EVENT,
+ Index.INPUT_EVENT_ID,
Index.HANDLE_INPUT_START,
Index.ANIMATION_START,
Index.PERFORM_TRAVERSALS_START,
@@ -225,8 +224,11 @@
Index.ISSUE_DRAW_COMMANDS_START,
Index.SWAP_BUFFERS,
Index.FRAME_COMPLETED,
+ Index.DEQUEUE_BUFFER_DURATION,
+ Index.QUEUE_BUFFER_DURATION,
Index.GPU_COMPLETED,
- Index.SWAP_BUFFERS_COMPLETED
+ Index.SWAP_BUFFERS_COMPLETED,
+ Index.DISPLAY_PRESENT_TIME,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Index {
@@ -234,20 +236,22 @@
int FRAME_TIMELINE_VSYNC_ID = 1;
int INTENDED_VSYNC = 2;
int VSYNC = 3;
- int OLDEST_INPUT_EVENT = 4;
- int NEWEST_INPUT_EVENT = 5;
- int HANDLE_INPUT_START = 6;
- int ANIMATION_START = 7;
- int PERFORM_TRAVERSALS_START = 8;
- int DRAW_START = 9;
- int FRAME_DEADLINE = 10;
- int SYNC_QUEUED = 11;
- int SYNC_START = 12;
- int ISSUE_DRAW_COMMANDS_START = 13;
- int SWAP_BUFFERS = 14;
- int FRAME_COMPLETED = 15;
- int GPU_COMPLETED = 18;
- int SWAP_BUFFERS_COMPLETED = 19;
+ int INPUT_EVENT_ID = 4;
+ int HANDLE_INPUT_START = 5;
+ int ANIMATION_START = 6;
+ int PERFORM_TRAVERSALS_START = 7;
+ int DRAW_START = 8;
+ int FRAME_DEADLINE = 9;
+ int SYNC_QUEUED = 10;
+ int SYNC_START = 11;
+ int ISSUE_DRAW_COMMANDS_START = 12;
+ int SWAP_BUFFERS = 13;
+ int FRAME_COMPLETED = 14;
+ int DEQUEUE_BUFFER_DURATION = 15;
+ int QUEUE_BUFFER_DURATION = 16;
+ int GPU_COMPLETED = 17;
+ int SWAP_BUFFERS_COMPLETED = 18;
+ int DISPLAY_PRESENT_TIME = 19;
int FRAME_STATS_COUNT = 20; // must always be last and in sync with
// FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 59e4931..fafb885 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -26,6 +26,7 @@
import android.hardware.SensorManager;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
+import android.hardware.lights.LightsManager;
import android.os.Build;
import android.os.NullVibrator;
import android.os.Parcel;
@@ -89,6 +90,9 @@
@GuardedBy("mMotionRanges")
private Battery mBattery;
+ @GuardedBy("mMotionRanges")
+ private LightsManager mLightsManager;
+
/**
* A mask for input source classes.
*
@@ -859,6 +863,21 @@
}
/**
+ * Gets the lights manager associated with the device, if there is one.
+ * Even if the device does not have lights, the result is never null.
+ * Use {@link LightsManager#getLights} to determine whether any lights is
+ * present.
+ *
+ * @return The lights manager associated with the device, never null.
+ */
+ public @NonNull LightsManager getLightsManager() {
+ if (mLightsManager == null) {
+ mLightsManager = InputManager.getInstance().getInputDeviceLightsManager(mId);
+ }
+ return mLightsManager;
+ }
+
+ /**
* Gets the sensor manager service associated with the input device.
* Even if the device does not have a sensor, the result is never null.
* Use {@link SensorManager#getSensorList} to get a full list of all supported sensors.
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 219190f..21dd1fb 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -20,8 +20,6 @@
import static android.view.InsetsStateProto.DISPLAY_FRAME;
import static android.view.InsetsStateProto.SOURCES;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
-import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
-import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.indexOf;
@@ -106,14 +104,11 @@
public static final int ITYPE_NAVIGATION_BAR = 1;
public static final int ITYPE_CAPTION_BAR = 2;
- // The always visible types are visible to all windows regardless of the z-order.
- public static final int FIRST_ALWAYS_VISIBLE_TYPE = 3;
- public static final int ITYPE_TOP_GESTURES = FIRST_ALWAYS_VISIBLE_TYPE;
+ public static final int ITYPE_TOP_GESTURES = 3;
public static final int ITYPE_BOTTOM_GESTURES = 4;
public static final int ITYPE_LEFT_GESTURES = 5;
public static final int ITYPE_RIGHT_GESTURES = 6;
- /** Additional gesture inset types that map into {@link Type.MANDATORY_SYSTEM_GESTURES}. */
public static final int ITYPE_TOP_MANDATORY_GESTURES = 7;
public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8;
public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
@@ -123,7 +118,6 @@
public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;
- public static final int LAST_ALWAYS_VISIBLE_TYPE = ITYPE_BOTTOM_DISPLAY_CUTOUT;
public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
@@ -188,18 +182,6 @@
}
/**
- * Mirror the always visible sources from the other state. They will share the same object for
- * the always visible types.
- *
- * @param other the state to mirror the mirrored sources from.
- */
- public void mirrorAlwaysVisibleInsetsSources(InsetsState other) {
- for (int type = FIRST_ALWAYS_VISIBLE_TYPE; type <= LAST_ALWAYS_VISIBLE_TYPE; type++) {
- mSources[type] = other.mSources[type];
- }
- }
-
- /**
* Calculates {@link WindowInsets} based on the current source configuration.
*
* @param frame The frame to calculate the insets relative to.
@@ -380,14 +362,14 @@
processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
insets, type);
- if (type == MANDATORY_SYSTEM_GESTURES) {
+ if (type == Type.MANDATORY_SYSTEM_GESTURES) {
// Mandatory system gestures are also system gestures.
// TODO: find a way to express this more generally. One option would be to define
// Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the
// ability to set systemGestureInsets() independently from
// mandatorySystemGestureInsets() in the Builder.
processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
- insets, SYSTEM_GESTURES);
+ insets, Type.SYSTEM_GESTURES);
}
}
@@ -493,9 +475,14 @@
* to the client.
*
* @param type The {@link InternalInsetsType} of the source to remove
+ * @return {@code true} if this InsetsState was modified; {@code false} otherwise.
*/
- public void removeSource(@InternalInsetsType int type) {
+ public boolean removeSource(@InternalInsetsType int type) {
+ if (mSources[type] == null) {
+ return false;
+ }
mSources[type] = null;
+ return true;
}
/**
@@ -552,6 +539,24 @@
}
}
+ /**
+ * Sets the values from the other InsetsState. But for sources, only specific types of source
+ * would be set.
+ *
+ * @param other the other InsetsState.
+ * @param types the only types of sources would be set.
+ */
+ public void set(InsetsState other, @InsetsType int types) {
+ mDisplayFrame.set(other.mDisplayFrame);
+ mDisplayCutout.set(other.mDisplayCutout);
+ mRoundedCorners = other.getRoundedCorners();
+ final ArraySet<Integer> t = toInternalType(types);
+ for (int i = t.size() - 1; i >= 0; i--) {
+ final int type = t.valueAt(i);
+ mSources[type] = other.mSources[type];
+ }
+ }
+
public void addSource(InsetsSource source) {
mSources[source.getType()] = source;
}
@@ -575,6 +580,18 @@
if ((types & Type.CAPTION_BAR) != 0) {
result.add(ITYPE_CAPTION_BAR);
}
+ if ((types & Type.SYSTEM_GESTURES) != 0) {
+ result.add(ITYPE_LEFT_GESTURES);
+ result.add(ITYPE_TOP_GESTURES);
+ result.add(ITYPE_RIGHT_GESTURES);
+ result.add(ITYPE_BOTTOM_GESTURES);
+ }
+ if ((types & Type.MANDATORY_SYSTEM_GESTURES) != 0) {
+ result.add(ITYPE_LEFT_MANDATORY_GESTURES);
+ result.add(ITYPE_TOP_MANDATORY_GESTURES);
+ result.add(ITYPE_RIGHT_MANDATORY_GESTURES);
+ result.add(ITYPE_BOTTOM_MANDATORY_GESTURES);
+ }
if ((types & Type.DISPLAY_CUTOUT) != 0) {
result.add(ITYPE_LEFT_DISPLAY_CUTOUT);
result.add(ITYPE_TOP_DISPLAY_CUTOUT);
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
index 015e804..569c287 100644
--- a/core/java/android/view/RoundedCorners.java
+++ b/core/java/android/view/RoundedCorners.java
@@ -335,7 +335,7 @@
}
if (o instanceof RoundedCorners) {
RoundedCorners r = (RoundedCorners) o;
- return Arrays.deepEquals(mRoundedCorners, ((RoundedCorners) o).mRoundedCorners);
+ return Arrays.deepEquals(mRoundedCorners, r.mRoundedCorners);
}
return false;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 66b9617..0832578 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -68,6 +68,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -141,6 +142,9 @@
int layerStack);
private static native void nativeSetBlurRegions(long transactionObj, long nativeObj,
float[][] regions, int length);
+ private static native void nativeSetStretchEffect(long transactionObj, long nativeObj,
+ float left, float top, float right, float bottom, float vecX, float vecY,
+ float maxStretchAmount);
private static native boolean nativeClearContentFrameStats(long nativeObject);
private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
@@ -161,25 +165,21 @@
int L, int T, int R, int B);
private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken,
int width, int height);
- private static native DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
- private static native DisplayMode[] nativeGetDisplayModes(
- IBinder displayToken);
+ private static native StaticDisplayInfo nativeGetStaticDisplayInfo(IBinder displayToken);
+ private static native DynamicDisplayInfo nativeGetDynamicDisplayInfo(IBinder displayToken);
private static native DisplayedContentSamplingAttributes
nativeGetDisplayedContentSamplingAttributes(IBinder displayToken);
private static native boolean nativeSetDisplayedContentSamplingEnabled(IBinder displayToken,
boolean enable, int componentMask, int maxFrames);
private static native DisplayedContentSample nativeGetDisplayedContentSample(
IBinder displayToken, long numFrames, long timestamp);
- private static native int nativeGetActiveDisplayMode(IBinder displayToken);
private static native boolean nativeSetDesiredDisplayModeSpecs(IBinder displayToken,
DesiredDisplayModeSpecs desiredDisplayModeSpecs);
private static native DesiredDisplayModeSpecs
nativeGetDesiredDisplayModeSpecs(IBinder displayToken);
- private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
private static native DisplayPrimaries nativeGetDisplayNativePrimaries(
IBinder displayToken);
private static native int[] nativeGetCompositionDataspaces();
- private static native int nativeGetActiveColorMode(IBinder displayToken);
private static native boolean nativeSetActiveColorMode(IBinder displayToken,
int colorMode);
private static native void nativeSetAutoLowLatencyMode(IBinder displayToken, boolean on);
@@ -191,11 +191,6 @@
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
- private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
-
- private static native boolean nativeGetAutoLowLatencyModeSupport(IBinder displayToken);
- private static native boolean nativeGetGameContentTypeSupport(IBinder displayToken);
-
private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
InputWindowHandle handle);
@@ -1707,7 +1702,7 @@
*
* @hide
*/
- public static final class DisplayInfo {
+ public static final class StaticDisplayInfo {
public boolean isInternal;
public float density;
public boolean secure;
@@ -1715,7 +1710,7 @@
@Override
public String toString() {
- return "DisplayInfo{isInternal=" + isInternal
+ return "StaticDisplayInfo{isInternal=" + isInternal
+ ", density=" + density
+ ", secure=" + secure
+ ", deviceProductInfo=" + deviceProductInfo + "}";
@@ -1725,7 +1720,7 @@
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- DisplayInfo that = (DisplayInfo) o;
+ StaticDisplayInfo that = (StaticDisplayInfo) o;
return isInternal == that.isInternal
&& density == that.density
&& secure == that.secure
@@ -1739,6 +1734,54 @@
}
/**
+ * Dynamic information about physical display.
+ *
+ * @hide
+ */
+ public static final class DynamicDisplayInfo {
+ public DisplayMode[] supportedDisplayModes;
+ public int activeDisplayModeId;
+
+ public int[] supportedColorModes;
+ public int activeColorMode;
+
+ public Display.HdrCapabilities hdrCapabilities;
+
+ public boolean autoLowLatencyModeSupported;
+ public boolean gameContentTypeSupported;
+
+ @Override
+ public String toString() {
+ return "DynamicDisplayInfo{"
+ + "supportedDisplayModes=" + Arrays.toString(supportedDisplayModes)
+ + ", activeDisplayModeId=" + activeDisplayModeId
+ + ", supportedColorModes=" + Arrays.toString(supportedColorModes)
+ + ", activeColorMode=" + activeColorMode
+ + ", hdrCapabilities=" + hdrCapabilities
+ + ", autoLowLatencyModeSupported=" + autoLowLatencyModeSupported
+ + ", gameContentTypeSupported" + gameContentTypeSupported + "}";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DynamicDisplayInfo that = (DynamicDisplayInfo) o;
+ return Arrays.equals(supportedDisplayModes, that.supportedDisplayModes)
+ && activeDisplayModeId == that.activeDisplayModeId
+ && Arrays.equals(supportedColorModes, that.supportedColorModes)
+ && activeColorMode == that.activeColorMode
+ && Objects.equals(hdrCapabilities, that.hdrCapabilities);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(supportedDisplayModes, activeDisplayModeId, activeDisplayModeId,
+ activeColorMode, hdrCapabilities);
+ }
+ }
+
+ /**
* Configuration supported by physical display.
*
* @hide
@@ -1749,6 +1792,7 @@
*/
public static final int INVALID_DISPLAY_MODE_ID = -1;
+ public int id;
public int width;
public int height;
public float xDpi;
@@ -1768,7 +1812,8 @@
@Override
public String toString() {
- return "DisplayConfig{width=" + width
+ return "DisplayMode{id=" + id
+ + ", width=" + width
+ ", height=" + height
+ ", xDpi=" + xDpi
+ ", yDpi=" + yDpi
@@ -1777,6 +1822,28 @@
+ ", presentationDeadlineNanos=" + presentationDeadlineNanos
+ ", group=" + group + "}";
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DisplayMode that = (DisplayMode) o;
+ return id == that.id
+ && width == that.width
+ && height == that.height
+ && Float.compare(that.xDpi, xDpi) == 0
+ && Float.compare(that.yDpi, yDpi) == 0
+ && Float.compare(that.refreshRate, refreshRate) == 0
+ && appVsyncOffsetNanos == that.appVsyncOffsetNanos
+ && presentationDeadlineNanos == that.presentationDeadlineNanos
+ && group == that.group;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, width, height, xDpi, yDpi, refreshRate, appVsyncOffsetNanos,
+ presentationDeadlineNanos, group);
+ }
}
/**
@@ -1792,31 +1859,21 @@
/**
* @hide
*/
- public static SurfaceControl.DisplayInfo getDisplayInfo(IBinder displayToken) {
+ public static StaticDisplayInfo getStaticDisplayInfo(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeGetDisplayInfo(displayToken);
+ return nativeGetStaticDisplayInfo(displayToken);
}
/**
* @hide
*/
- public static DisplayMode[] getDisplayModes(IBinder displayToken) {
+ public static DynamicDisplayInfo getDynamicDisplayInfo(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeGetDisplayModes(displayToken);
- }
-
- /**
- * @hide
- */
- public static int getActiveDisplayMode(IBinder displayToken) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- return nativeGetActiveDisplayMode(displayToken);
+ return nativeGetDynamicDisplayInfo(displayToken);
}
/**
@@ -1978,16 +2035,6 @@
}
/**
- * @hide
- */
- public static int[] getDisplayColorModes(IBinder displayToken) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- return nativeGetDisplayColorModes(displayToken);
- }
-
- /**
* Color coordinates in CIE1931 XYZ color space
*
* @hide
@@ -2057,16 +2104,6 @@
/**
* @hide
*/
- public static int getActiveColorMode(IBinder displayToken) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- return nativeGetActiveColorMode(displayToken);
- }
-
- /**
- * @hide
- */
public static boolean setActiveColorMode(IBinder displayToken, int colorMode) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
@@ -2169,38 +2206,6 @@
/**
* @hide
*/
- public static Display.HdrCapabilities getHdrCapabilities(IBinder displayToken) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- return nativeGetHdrCapabilities(displayToken);
- }
-
- /**
- * @hide
- */
- public static boolean getAutoLowLatencyModeSupport(IBinder displayToken) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
-
- return nativeGetAutoLowLatencyModeSupport(displayToken);
- }
-
- /**
- * @hide
- */
- public static boolean getGameContentTypeSupport(IBinder displayToken) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
-
- return nativeGetGameContentTypeSupport(displayToken);
- }
-
- /**
- * @hide
- */
@UnsupportedAppUsage
public static IBinder createDisplay(String name, boolean secure) {
if (name == null) {
@@ -2951,6 +2956,17 @@
/**
* @hide
*/
+ public Transaction setStretchEffect(SurfaceControl sc, float left, float top, float right,
+ float bottom, float vecX, float vecY, float maxStretchAmount) {
+ checkPreconditions(sc);
+ nativeSetStretchEffect(mNativeObject, sc.mNativeObject, left, top, right, bottom,
+ vecX, vecY, maxStretchAmount);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
checkPreconditions(sc);
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 18029af..870fd8c 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -218,16 +218,6 @@
}
/**
- * @hide
- */
- @TestApi
- public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) {
- Objects.requireNonNull(view);
- view.setLayoutParams(attrs);
- mViewRoot.setView(view, attrs, null);
- }
-
- /**
* Set the root view of the SurfaceControlViewHost. This view will render in to
* the SurfaceControl, and receive input based on the SurfaceControls positioning on
* screen. It will be laid as if it were in a window of the passed in width and height.
@@ -240,11 +230,21 @@
final WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(width, height,
WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
- lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
setView(view, lp);
}
/**
+ * @hide
+ */
+ @TestApi
+ public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) {
+ Objects.requireNonNull(view);
+ attrs.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ view.setLayoutParams(attrs);
+ mViewRoot.setView(view, attrs, null);
+ }
+
+ /**
* @return The view passed to setView, or null if none has been passed.
*/
public @Nullable View getView() {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6eba83f..ec7e4c1 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1444,6 +1444,14 @@
}
@Override
+ public void applyStretch(long frameNumber, float left, float top, float right,
+ float bottom, float vecX, float vecY, float maxStretch) {
+ mRtTransaction.setStretchEffect(mSurfaceControl, left, top, right, bottom, vecX, vecY,
+ maxStretch);
+ applyRtTransaction(frameNumber);
+ }
+
+ @Override
public void positionLost(long frameNumber) {
if (DEBUG) {
Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c46fbc7..3789324 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6996,6 +6996,7 @@
* @see #getScrollIndicators()
* @attr ref android.R.styleable#View_scrollIndicators
*/
+ @RemotableViewMethod
public void setScrollIndicators(@ScrollIndicators int indicators) {
setScrollIndicators(indicators,
SCROLL_INDICATORS_PFLAG3_MASK >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT);
@@ -11881,6 +11882,7 @@
* @see #setFocusable(int)
* @attr ref android.R.styleable#View_focusable
*/
+ @RemotableViewMethod
public void setFocusable(boolean focusable) {
setFocusable(focusable ? FOCUSABLE : NOT_FOCUSABLE);
}
@@ -11899,6 +11901,7 @@
* @see #setFocusableInTouchMode(boolean)
* @attr ref android.R.styleable#View_focusable
*/
+ @RemotableViewMethod
public void setFocusable(@Focusable int focusable) {
if ((focusable & (FOCUSABLE_AUTO | FOCUSABLE)) == 0) {
setFlags(0, FOCUSABLE_IN_TOUCH_MODE);
@@ -11917,6 +11920,7 @@
* @see #setFocusable(boolean)
* @attr ref android.R.styleable#View_focusableInTouchMode
*/
+ @RemotableViewMethod
public void setFocusableInTouchMode(boolean focusableInTouchMode) {
// Focusable in touch mode should always be set before the focusable flag
// otherwise, setting the focusable flag will trigger a focusableViewAvailable()
@@ -12871,6 +12875,7 @@
*
* @attr ref android.R.styleable#View_focusedByDefault
*/
+ @RemotableViewMethod
public void setFocusedByDefault(boolean isFocusedByDefault) {
if (isFocusedByDefault == ((mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0)) {
return;
@@ -16850,6 +16855,7 @@
*
* @attr ref android.R.styleable#View_rotation
*/
+ @RemotableViewMethod
public void setRotation(float rotation) {
if (rotation != getRotation()) {
// Double-invalidation is necessary to capture view's old and new areas
@@ -16896,6 +16902,7 @@
*
* @attr ref android.R.styleable#View_rotationY
*/
+ @RemotableViewMethod
public void setRotationY(float rotationY) {
if (rotationY != getRotationY()) {
invalidateViewProperty(true, false);
@@ -16941,6 +16948,7 @@
*
* @attr ref android.R.styleable#View_rotationX
*/
+ @RemotableViewMethod
public void setRotationX(float rotationX) {
if (rotationX != getRotationX()) {
invalidateViewProperty(true, false);
@@ -16978,6 +16986,7 @@
*
* @attr ref android.R.styleable#View_scaleX
*/
+ @RemotableViewMethod
public void setScaleX(float scaleX) {
if (scaleX != getScaleX()) {
scaleX = sanitizeFloatPropertyValue(scaleX, "scaleX");
@@ -17016,6 +17025,7 @@
*
* @attr ref android.R.styleable#View_scaleY
*/
+ @RemotableViewMethod
public void setScaleY(float scaleY) {
if (scaleY != getScaleY()) {
scaleY = sanitizeFloatPropertyValue(scaleY, "scaleY");
@@ -17061,6 +17071,7 @@
*
* @attr ref android.R.styleable#View_transformPivotX
*/
+ @RemotableViewMethod
public void setPivotX(float pivotX) {
if (!mRenderNode.isPivotExplicitlySet() || pivotX != getPivotX()) {
invalidateViewProperty(true, false);
@@ -17103,6 +17114,7 @@
*
* @attr ref android.R.styleable#View_transformPivotY
*/
+ @RemotableViewMethod
public void setPivotY(float pivotY) {
if (!mRenderNode.isPivotExplicitlySet() || pivotY != getPivotY()) {
invalidateViewProperty(true, false);
@@ -17243,6 +17255,7 @@
*
* @attr ref android.R.styleable#View_alpha
*/
+ @RemotableViewMethod
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
@@ -17732,6 +17745,7 @@
*
* @attr ref android.R.styleable#View_elevation
*/
+ @RemotableViewMethod
public void setElevation(float elevation) {
if (elevation != getElevation()) {
elevation = sanitizeFloatPropertyValue(elevation, "elevation");
@@ -17766,6 +17780,7 @@
*
* @attr ref android.R.styleable#View_translationX
*/
+ @RemotableViewMethod
public void setTranslationX(float translationX) {
if (translationX != getTranslationX()) {
invalidateViewProperty(true, false);
@@ -17801,6 +17816,7 @@
*
* @attr ref android.R.styleable#View_translationY
*/
+ @RemotableViewMethod
public void setTranslationY(float translationY) {
if (translationY != getTranslationY()) {
invalidateViewProperty(true, false);
@@ -17828,6 +17844,7 @@
*
* @attr ref android.R.styleable#View_translationZ
*/
+ @RemotableViewMethod
public void setTranslationZ(float translationZ) {
if (translationZ != getTranslationZ()) {
translationZ = sanitizeFloatPropertyValue(translationZ, "translationZ");
@@ -23989,6 +24006,7 @@
* @see #getBackgroundTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setBackgroundTintList(@Nullable ColorStateList tint) {
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
@@ -24248,6 +24266,7 @@
* @see #getForegroundTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setForegroundTintList(@Nullable ColorStateList tint) {
if (mForegroundInfo == null) {
mForegroundInfo = new ForegroundInfo();
diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java
index 890d071..d4aaa61 100644
--- a/core/java/android/view/ViewFrameInfo.java
+++ b/core/java/android/view/ViewFrameInfo.java
@@ -58,8 +58,8 @@
public void populateFrameInfo(FrameInfo frameInfo) {
frameInfo.frameInfo[FrameInfo.FLAGS] |= flags;
frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart;
- frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT] = oldestInputEventTime;
- frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT] = newestInputEventTime;
+ // TODO(b/169866723): Use InputEventAssigner
+ frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID] = newestInputEventTime;
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f1f6786..144691d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1677,7 +1677,8 @@
// See comment for View.sForceLayoutWhenInsetsChanged
if (View.sForceLayoutWhenInsetsChanged && mView != null
- && mWindowAttributes.softInputMode == SOFT_INPUT_ADJUST_RESIZE) {
+ && (mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST)
+ == SOFT_INPUT_ADJUST_RESIZE) {
forceLayout(mView);
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index decbf8c..4f0c568 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1975,7 +1975,10 @@
if (client == null) {
return false;
}
-
+ if (mService == null) {
+ Log.w(TAG, "Autofill service is null!");
+ return false;
+ }
if (mServiceClient == null) {
mServiceClient = new AutofillManagerClient(this);
try {
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index de4554b..6ade5e6 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -105,7 +105,7 @@
*/
@MainThread
default void initializeInternal(IBinder token, int displayId,
- IInputMethodPrivilegedOperations privilegedOperations) {
+ IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
updateInputMethodDisplay(displayId);
attachToken(token);
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 5d876a6..25712f8 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -18,19 +18,23 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -60,6 +64,7 @@
* @attr ref android.R.styleable#InputMethod_isDefault
* @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
* @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
+ * @attr ref android.R.styleable#InputMethod_configChanges
*/
public final class InputMethodInfo implements Parcelable {
static final String TAG = "InputMethodInfo";
@@ -118,6 +123,12 @@
private final boolean mInlineSuggestionsEnabled;
/**
+ * The flag for configurations IME assumes the responsibility for handling in
+ * {@link InputMethodService#onConfigurationChanged(Configuration)}}.
+ */
+ private final int mHandledConfigChanges;
+
+ /**
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
* @return a unique ID to be returned by {@link #getId()}. We have used
* {@link ComponentName#flattenToShortString()} for this purpose (and it is already
@@ -203,6 +214,8 @@
false);
inlineSuggestionsEnabled = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false);
+ mHandledConfigChanges = sa.getInt(
+ com.android.internal.R.styleable.InputMethod_configChanges, 0);
sa.recycle();
final int depth = parser.getDepth();
@@ -287,6 +300,7 @@
mIsVrOnly = source.readBoolean();
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
+ mHandledConfigChanges = source.readInt();
mForceDefault = false;
}
@@ -298,7 +312,22 @@
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
- false /* inlineSuggestionsEnabled */, false /* isVrOnly */);
+ false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
+ 0 /* handledConfigChanges */);
+ }
+
+ /**
+ * Temporary API for creating a built-in input method for test.
+ * @hide
+ */
+ @TestApi
+ public InputMethodInfo(@NonNull String packageName, @NonNull String className,
+ @NonNull CharSequence label, @NonNull String settingsActivity,
+ int handledConfigChanges) {
+ this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
+ settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
+ false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+ false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges);
}
/**
@@ -310,7 +339,7 @@
boolean forceDefault) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
- false /* isVrOnly */);
+ false /* isVrOnly */, 0 /* handledconfigChanges */);
}
/**
@@ -321,7 +350,8 @@
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
- supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly);
+ supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
+ 0 /* handledConfigChanges */);
}
/**
@@ -331,7 +361,7 @@
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
- boolean isVrOnly) {
+ boolean isVrOnly, int handledConfigChanges) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -343,6 +373,7 @@
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
mIsVrOnly = isVrOnly;
+ mHandledConfigChanges = handledConfigChanges;
}
private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
@@ -489,6 +520,17 @@
}
}
+ /**
+ * Returns the bit mask of kinds of configuration changes that this IME
+ * can handle itself (without being restarted by the system).
+ *
+ * @attr ref android.R.styleable#InputMethod_configChanges
+ */
+ @ActivityInfo.Config
+ public int getConfigChanges() {
+ return mHandledConfigChanges;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
@@ -579,6 +621,7 @@
dest.writeBoolean(mIsVrOnly);
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
+ dest.writeInt(mHandledConfigChanges);
}
/**
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d5f9774..39c09b4 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -715,7 +715,7 @@
*/
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769408)
- private EdgeEffect mEdgeGlowTop = new EdgeEffect(mContext);
+ private EdgeEffect mEdgeGlowTop;
/**
* Tracks the state of the bottom edge glow.
@@ -725,7 +725,7 @@
*/
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768444)
- private EdgeEffect mEdgeGlowBottom = new EdgeEffect(mContext);
+ private EdgeEffect mEdgeGlowBottom;
/**
* An estimate of how many pixels are between the top of the list and
@@ -847,6 +847,8 @@
public AbsListView(Context context) {
super(context);
+ mEdgeGlowBottom = new EdgeEffect(context);
+ mEdgeGlowTop = new EdgeEffect(context);
initAbsListView();
mOwnerThread = Thread.currentThread();
@@ -867,6 +869,8 @@
public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mEdgeGlowBottom = new EdgeEffect(context, attrs);
+ mEdgeGlowTop = new EdgeEffect(context, attrs);
initAbsListView();
mOwnerThread = Thread.currentThread();
@@ -3584,6 +3588,9 @@
mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY;
int lastYCorrection = 0;
+ // First allow releasing existing overscroll effect:
+ incrementalDeltaY = releaseGlow(incrementalDeltaY, x);
+
if (mTouchMode == TOUCH_MODE_SCROLL) {
if (PROFILE_SCROLLING) {
if (!mScrollProfilingStarted) {
@@ -3666,14 +3673,14 @@
mTouchMode = TOUCH_MODE_OVERSCROLL;
}
if (incrementalDeltaY > 0) {
- mEdgeGlowTop.onPull((float) -overscroll / getHeight(),
+ mEdgeGlowTop.onPullDistance((float) -overscroll / getHeight(),
(float) x / getWidth());
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
invalidateTopGlow();
} else if (incrementalDeltaY < 0) {
- mEdgeGlowBottom.onPull((float) overscroll / getHeight(),
+ mEdgeGlowBottom.onPullDistance((float) overscroll / getHeight(),
1.f - (float) x / getWidth());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
@@ -3713,14 +3720,15 @@
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
!contentFits())) {
if (rawDeltaY > 0) {
- mEdgeGlowTop.onPull((float) overScrollDistance / getHeight(),
+ mEdgeGlowTop.onPullDistance((float) overScrollDistance / getHeight(),
(float) x / getWidth());
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
invalidateTopGlow();
} else if (rawDeltaY < 0) {
- mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(),
+ mEdgeGlowBottom.onPullDistance(
+ (float) -overScrollDistance / getHeight(),
1.f - (float) x / getWidth());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
@@ -3757,6 +3765,44 @@
}
}
+ /**
+ * If the edge glow is 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 horizontal position of the pointer.
+ * @return The remainder of <code>deltaY</code> that has not been consumed by the
+ * edge glow.
+ */
+ private int releaseGlow(int deltaY, int x) {
+ // First allow releasing existing overscroll effect:
+ float consumed = 0;
+ if (mEdgeGlowTop.getDistance() != 0) {
+ consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(),
+ (float) x / getWidth());
+ if (consumed != 0f) {
+ invalidateTopGlow();
+ }
+ } else if (mEdgeGlowBottom.getDistance() != 0) {
+ consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(),
+ 1f - (float) x / getWidth());
+ if (consumed != 0f) {
+ invalidateBottomGlow();
+ }
+ }
+ int pixelsConsumed = Math.round(consumed * getHeight());
+ return deltaY - pixelsConsumed;
+ }
+
+ /**
+ * @return <code>true</code> if either the top or bottom edge glow is currently active or
+ * <code>false</code> if it has no value to release.
+ */
+ private boolean isGlowActive() {
+ return mEdgeGlowBottom.getDistance() != 0 || mEdgeGlowTop.getDistance() != 0;
+ }
+
private void invalidateTopGlow() {
if (!shouldDisplayEdgeEffects()) {
return;
@@ -3926,7 +3972,9 @@
if (mTouchMode == TOUCH_MODE_OVERFLING) {
// Stopped the fling. It is a scroll.
- mFlingRunnable.endFling();
+ if (mFlingRunnable != null) {
+ mFlingRunnable.endFling();
+ }
if (mPositionScroller != null) {
mPositionScroller.stop();
}
@@ -3936,6 +3984,7 @@
mLastY = mMotionY;
mMotionCorrection = 0;
mDirection = 0;
+ stopEdgeGlowRecede(ev.getX());
} else {
final int x = (int) ev.getX();
final int y = (int) ev.getY();
@@ -3948,7 +3997,10 @@
mTouchMode = TOUCH_MODE_SCROLL;
mMotionCorrection = 0;
motionPosition = findMotionRow(y);
- mFlingRunnable.flywheelTouch();
+ if (mFlingRunnable != null) {
+ mFlingRunnable.flywheelTouch();
+ }
+ stopEdgeGlowRecede(x);
} else if ((motionPosition >= 0) && getAdapter().isEnabled(motionPosition)) {
// User clicked on an actual view (and was not stopping a
// fling). It might be a click or a scroll. Assume it is a
@@ -3984,6 +4036,15 @@
}
}
+ private void stopEdgeGlowRecede(float x) {
+ if (mEdgeGlowTop.getDistance() != 0) {
+ mEdgeGlowTop.onPullDistance(0, x / getWidth());
+ }
+ if (mEdgeGlowBottom.getDistance() != 0) {
+ mEdgeGlowBottom.onPullDistance(0, x / getWidth());
+ }
+ }
+
private void onTouchMove(MotionEvent ev, MotionEvent vtev) {
if (mHasPerformedLongPress) {
// Consume all move events following a successful long press.
@@ -4489,73 +4550,76 @@
}
switch (actionMasked) {
- case MotionEvent.ACTION_DOWN: {
- int touchMode = mTouchMode;
- if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) {
- mMotionCorrection = 0;
- return true;
- }
-
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
- mActivePointerId = ev.getPointerId(0);
-
- int motionPosition = findMotionRow(y);
- if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {
- // User clicked on an actual view (and was not stopping a fling).
- // Remember where the motion event started
- v = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = v.getTop();
- mMotionX = x;
- mMotionY = y;
- mMotionPosition = motionPosition;
- mTouchMode = TOUCH_MODE_DOWN;
- clearScrollingCache();
- }
- mLastY = Integer.MIN_VALUE;
- initOrResetVelocityTracker();
- mVelocityTracker.addMovement(ev);
- mNestedYOffset = 0;
- startNestedScroll(SCROLL_AXIS_VERTICAL);
- if (touchMode == TOUCH_MODE_FLING) {
- return true;
- }
- break;
- }
-
- case MotionEvent.ACTION_MOVE: {
- switch (mTouchMode) {
- case TOUCH_MODE_DOWN:
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == -1) {
- pointerIndex = 0;
- mActivePointerId = ev.getPointerId(pointerIndex);
+ case MotionEvent.ACTION_DOWN: {
+ int touchMode = mTouchMode;
+ if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) {
+ mMotionCorrection = 0;
+ return true;
}
- final int y = (int) ev.getY(pointerIndex);
- initVelocityTrackerIfNotExists();
+
+ final int x = (int) ev.getX();
+ final int y = (int) ev.getY();
+ mActivePointerId = ev.getPointerId(0);
+
+ int motionPosition = findMotionRow(y);
+ if (isGlowActive()) {
+ // Pressed during edge effect, so this is considered the same as a fling catch.
+ mTouchMode = TOUCH_MODE_FLING;
+ } else if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {
+ // User clicked on an actual view (and was not stopping a fling).
+ // Remember where the motion event started
+ v = getChildAt(motionPosition - mFirstPosition);
+ mMotionViewOriginalTop = v.getTop();
+ mMotionX = x;
+ mMotionY = y;
+ mMotionPosition = motionPosition;
+ mTouchMode = TOUCH_MODE_DOWN;
+ clearScrollingCache();
+ }
+ mLastY = Integer.MIN_VALUE;
+ initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
- if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) {
+ mNestedYOffset = 0;
+ startNestedScroll(SCROLL_AXIS_VERTICAL);
+ if (touchMode == TOUCH_MODE_FLING) {
return true;
}
break;
}
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- mTouchMode = TOUCH_MODE_REST;
- mActivePointerId = INVALID_POINTER;
- recycleVelocityTracker();
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- stopNestedScroll();
- break;
- }
+ case MotionEvent.ACTION_MOVE: {
+ switch (mTouchMode) {
+ case TOUCH_MODE_DOWN:
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == -1) {
+ pointerIndex = 0;
+ mActivePointerId = ev.getPointerId(pointerIndex);
+ }
+ final int y = (int) ev.getY(pointerIndex);
+ initVelocityTrackerIfNotExists();
+ mVelocityTracker.addMovement(ev);
+ if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) {
+ return true;
+ }
+ break;
+ }
+ break;
+ }
- case MotionEvent.ACTION_POINTER_UP: {
- onSecondaryPointerUp(ev);
- break;
- }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP: {
+ mTouchMode = TOUCH_MODE_REST;
+ mActivePointerId = INVALID_POINTER;
+ recycleVelocityTracker();
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ stopNestedScroll();
+ break;
+ }
+
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(ev);
+ break;
+ }
}
return false;
@@ -6548,6 +6612,27 @@
}
/**
+ * Returns the {@link EdgeEffect#getType()} for the edge effects.
+ * @return the {@link EdgeEffect#getType()} for the edge effects.
+ * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+ */
+ @EdgeEffect.EdgeEffectType
+ public int getEdgeEffectType() {
+ return mEdgeGlowTop.getType();
+ }
+
+ /**
+ * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
+ * @param type The edge effect type to use for the edge effects.
+ * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+ */
+ public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
+ mEdgeGlowTop.setType(type);
+ mEdgeGlowBottom.setType(type);
+ invalidate();
+ }
+
+ /**
* Sets the recycler listener to be notified whenever a View is set aside in
* the recycler for later reuse. This listener can be used to free resources
* associated to the View.
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 93b2d8a..34fe51e 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -23,8 +23,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -68,12 +70,16 @@
@UnsupportedAppUsage
private Drawable mHourHand;
+ private final TintInfo mHourHandTintInfo = new TintInfo();
@UnsupportedAppUsage
private Drawable mMinuteHand;
+ private final TintInfo mMinuteHandTintInfo = new TintInfo();
@Nullable
private Drawable mSecondHand;
+ private final TintInfo mSecondHandTintInfo = new TintInfo();
@UnsupportedAppUsage
private Drawable mDial;
+ private final TintInfo mDialTintInfo = new TintInfo();
private int mDialWidth;
private int mDialHeight;
@@ -111,18 +117,86 @@
mDial = context.getDrawable(com.android.internal.R.drawable.clock_dial);
}
+ ColorStateList dialTintList = a.getColorStateList(
+ com.android.internal.R.styleable.AnalogClock_dialTint);
+ if (dialTintList != null) {
+ mDialTintInfo.mTintList = dialTintList;
+ mDialTintInfo.mHasTintList = true;
+ }
+ BlendMode dialTintMode = Drawable.parseBlendMode(
+ a.getInt(com.android.internal.R.styleable.AnalogClock_dialTintMode, -1),
+ null);
+ if (dialTintMode != null) {
+ mDialTintInfo.mTintBlendMode = dialTintMode;
+ mDialTintInfo.mHasTintBlendMode = true;
+ }
+ if (mDialTintInfo.mHasTintList || mDialTintInfo.mHasTintBlendMode) {
+ mDial = mDialTintInfo.apply(mDial);
+ }
+
mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour);
if (mHourHand == null) {
mHourHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_hour);
}
+ ColorStateList hourHandTintList = a.getColorStateList(
+ com.android.internal.R.styleable.AnalogClock_hand_hourTint);
+ if (hourHandTintList != null) {
+ mHourHandTintInfo.mTintList = hourHandTintList;
+ mHourHandTintInfo.mHasTintList = true;
+ }
+ BlendMode hourHandTintMode = Drawable.parseBlendMode(
+ a.getInt(com.android.internal.R.styleable.AnalogClock_hand_hourTintMode, -1),
+ null);
+ if (hourHandTintMode != null) {
+ mHourHandTintInfo.mTintBlendMode = hourHandTintMode;
+ mHourHandTintInfo.mHasTintBlendMode = true;
+ }
+ if (mHourHandTintInfo.mHasTintList || mHourHandTintInfo.mHasTintBlendMode) {
+ mHourHand = mHourHandTintInfo.apply(mHourHand);
+ }
+
mMinuteHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_minute);
if (mMinuteHand == null) {
mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute);
}
+ ColorStateList minuteHandTintList = a.getColorStateList(
+ com.android.internal.R.styleable.AnalogClock_hand_minuteTint);
+ if (minuteHandTintList != null) {
+ mMinuteHandTintInfo.mTintList = minuteHandTintList;
+ mMinuteHandTintInfo.mHasTintList = true;
+ }
+ BlendMode minuteHandTintMode = Drawable.parseBlendMode(
+ a.getInt(com.android.internal.R.styleable.AnalogClock_hand_minuteTintMode, -1),
+ null);
+ if (minuteHandTintMode != null) {
+ mMinuteHandTintInfo.mTintBlendMode = minuteHandTintMode;
+ mMinuteHandTintInfo.mHasTintBlendMode = true;
+ }
+ if (mMinuteHandTintInfo.mHasTintList || mMinuteHandTintInfo.mHasTintBlendMode) {
+ mMinuteHand = mMinuteHandTintInfo.apply(mMinuteHand);
+ }
+
mSecondHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_second);
+ ColorStateList secondHandTintList = a.getColorStateList(
+ com.android.internal.R.styleable.AnalogClock_hand_secondTint);
+ if (secondHandTintList != null) {
+ mSecondHandTintInfo.mTintList = secondHandTintList;
+ mSecondHandTintInfo.mHasTintList = true;
+ }
+ BlendMode secondHandTintMode = Drawable.parseBlendMode(
+ a.getInt(com.android.internal.R.styleable.AnalogClock_hand_secondTintMode, -1),
+ null);
+ if (secondHandTintMode != null) {
+ mSecondHandTintInfo.mTintBlendMode = secondHandTintMode;
+ mSecondHandTintInfo.mHasTintBlendMode = true;
+ }
+ if (mSecondHandTintInfo.mHasTintList || mSecondHandTintInfo.mHasTintBlendMode) {
+ mSecondHand = mSecondHandTintInfo.apply(mSecondHand);
+ }
+
mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone));
createClock();
@@ -141,6 +215,68 @@
invalidate();
}
+ /**
+ * Applies a tint to the dial drawable.
+ * <p>
+ * Subsequent calls to {@link #setDial(Icon)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#AnalogClock_dialTint
+ * @see #getDialTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ @RemotableViewMethod
+ public void setDialTintList(@Nullable ColorStateList tint) {
+ mDialTintInfo.mTintList = tint;
+ mDialTintInfo.mHasTintList = true;
+
+ mDial = mDialTintInfo.apply(mDial);
+ }
+
+ /**
+ * @return the tint applied to the dial drawable
+ * @attr ref android.R.styleable#AnalogClock_dialTint
+ * @see #setDialTintList(ColorStateList)
+ */
+ @InspectableProperty(attributeId = com.android.internal.R.styleable.AnalogClock_dialTint)
+ @Nullable
+ public ColorStateList getDialTintList() {
+ return mDialTintInfo.mTintList;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setDialTintList(ColorStateList)}} to the dial drawable.
+ * The default mode is {@link BlendMode#SRC_IN}.
+ *
+ * @param blendMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#AnalogClock_dialTintMode
+ * @see #getDialTintBlendMode()
+ * @see Drawable#setTintBlendMode(BlendMode)
+ */
+ @RemotableViewMethod
+ public void setDialTintBlendMode(@Nullable BlendMode blendMode) {
+ mDialTintInfo.mTintBlendMode = blendMode;
+ mDialTintInfo.mHasTintBlendMode = true;
+
+ mDial = mDialTintInfo.apply(mDial);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the dial drawable
+ * @attr ref android.R.styleable#AnalogClock_dialTintMode
+ * @see #setDialTintBlendMode(BlendMode)
+ */
+ @InspectableProperty(attributeId = com.android.internal.R.styleable.AnalogClock_dialTintMode)
+ @Nullable
+ public BlendMode getDialTintBlendMode() {
+ return mDialTintInfo.mTintBlendMode;
+ }
+
/** Sets the hour hand of the clock to the specified Icon. */
@RemotableViewMethod
public void setHourHand(@NonNull Icon icon) {
@@ -150,6 +286,71 @@
invalidate();
}
+ /**
+ * Applies a tint to the hour hand drawable.
+ * <p>
+ * Subsequent calls to {@link #setHourHand(Icon)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#AnalogClock_hand_hourTint
+ * @see #getHourHandTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ @RemotableViewMethod
+ public void setHourHandTintList(@Nullable ColorStateList tint) {
+ mHourHandTintInfo.mTintList = tint;
+ mHourHandTintInfo.mHasTintList = true;
+
+ mHourHand = mHourHandTintInfo.apply(mHourHand);
+ }
+
+ /**
+ * @return the tint applied to the hour hand drawable
+ * @attr ref android.R.styleable#AnalogClock_hand_hourTint
+ * @see #setHourHandTintList(ColorStateList)
+ */
+ @InspectableProperty(
+ attributeId = com.android.internal.R.styleable.AnalogClock_hand_hourTint
+ )
+ @Nullable
+ public ColorStateList getHourHandTintList() {
+ return mHourHandTintInfo.mTintList;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setHourHandTintList(ColorStateList)}} to the hour hand drawable.
+ * The default mode is {@link BlendMode#SRC_IN}.
+ *
+ * @param blendMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#AnalogClock_hand_hourTintMode
+ * @see #getHourHandTintBlendMode()
+ * @see Drawable#setTintBlendMode(BlendMode)
+ */
+ @RemotableViewMethod
+ public void setHourHandTintBlendMode(@Nullable BlendMode blendMode) {
+ mHourHandTintInfo.mTintBlendMode = blendMode;
+ mHourHandTintInfo.mHasTintBlendMode = true;
+
+ mHourHand = mHourHandTintInfo.apply(mHourHand);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the hour hand drawable
+ * @attr ref android.R.styleable#AnalogClock_hand_hourTintMode
+ * @see #setHourHandTintBlendMode(BlendMode)
+ */
+ @InspectableProperty(
+ attributeId = com.android.internal.R.styleable.AnalogClock_hand_hourTintMode)
+ @Nullable
+ public BlendMode getHourHandTintBlendMode() {
+ return mHourHandTintInfo.mTintBlendMode;
+ }
+
/** Sets the minute hand of the clock to the specified Icon. */
@RemotableViewMethod
public void setMinuteHand(@NonNull Icon icon) {
@@ -160,6 +361,71 @@
}
/**
+ * Applies a tint to the minute hand drawable.
+ * <p>
+ * Subsequent calls to {@link #setMinuteHand(Icon)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#AnalogClock_hand_minuteTint
+ * @see #getMinuteHandTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ @RemotableViewMethod
+ public void setMinuteHandTintList(@Nullable ColorStateList tint) {
+ mMinuteHandTintInfo.mTintList = tint;
+ mMinuteHandTintInfo.mHasTintList = true;
+
+ mMinuteHand = mMinuteHandTintInfo.apply(mMinuteHand);
+ }
+
+ /**
+ * @return the tint applied to the minute hand drawable
+ * @attr ref android.R.styleable#AnalogClock_hand_minuteTint
+ * @see #setMinuteHandTintList(ColorStateList)
+ */
+ @InspectableProperty(
+ attributeId = com.android.internal.R.styleable.AnalogClock_hand_minuteTint
+ )
+ @Nullable
+ public ColorStateList getMinuteHandTintList() {
+ return mMinuteHandTintInfo.mTintList;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setMinuteHandTintList(ColorStateList)}} to the minute hand drawable.
+ * The default mode is {@link BlendMode#SRC_IN}.
+ *
+ * @param blendMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#AnalogClock_hand_minuteTintMode
+ * @see #getMinuteHandTintBlendMode()
+ * @see Drawable#setTintBlendMode(BlendMode)
+ */
+ @RemotableViewMethod
+ public void setMinuteHandTintBlendMode(@Nullable BlendMode blendMode) {
+ mMinuteHandTintInfo.mTintBlendMode = blendMode;
+ mMinuteHandTintInfo.mHasTintBlendMode = true;
+
+ mMinuteHand = mMinuteHandTintInfo.apply(mMinuteHand);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the minute hand drawable
+ * @attr ref android.R.styleable#AnalogClock_hand_minuteTintMode
+ * @see #setMinuteHandTintBlendMode(BlendMode)
+ */
+ @InspectableProperty(
+ attributeId = com.android.internal.R.styleable.AnalogClock_hand_minuteTintMode)
+ @Nullable
+ public BlendMode getMinuteHandTintBlendMode() {
+ return mMinuteHandTintInfo.mTintBlendMode;
+ }
+
+ /**
* Sets the second hand of the clock to the specified Icon, or hides the second hand if it is
* null.
*/
@@ -173,6 +439,71 @@
}
/**
+ * Applies a tint to the second hand drawable.
+ * <p>
+ * Subsequent calls to {@link #setSecondHand(Icon)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#AnalogClock_hand_secondTint
+ * @see #getSecondHandTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ @RemotableViewMethod
+ public void setSecondHandTintList(@Nullable ColorStateList tint) {
+ mSecondHandTintInfo.mTintList = tint;
+ mSecondHandTintInfo.mHasTintList = true;
+
+ mSecondHand = mSecondHandTintInfo.apply(mSecondHand);
+ }
+
+ /**
+ * @return the tint applied to the second hand drawable
+ * @attr ref android.R.styleable#AnalogClock_hand_secondTint
+ * @see #setSecondHandTintList(ColorStateList)
+ */
+ @InspectableProperty(
+ attributeId = com.android.internal.R.styleable.AnalogClock_hand_secondTint
+ )
+ @Nullable
+ public ColorStateList getSecondHandTintList() {
+ return mSecondHandTintInfo.mTintList;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setSecondHandTintList(ColorStateList)}} to the second hand drawable.
+ * The default mode is {@link BlendMode#SRC_IN}.
+ *
+ * @param blendMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#AnalogClock_hand_secondTintMode
+ * @see #getSecondHandTintBlendMode()
+ * @see Drawable#setTintBlendMode(BlendMode)
+ */
+ @RemotableViewMethod
+ public void setSecondHandTintBlendMode(@Nullable BlendMode blendMode) {
+ mSecondHandTintInfo.mTintBlendMode = blendMode;
+ mSecondHandTintInfo.mHasTintBlendMode = true;
+
+ mSecondHand = mSecondHandTintInfo.apply(mSecondHand);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the second hand drawable
+ * @attr ref android.R.styleable#AnalogClock_hand_secondTintMode
+ * @see #setSecondHandTintBlendMode(BlendMode)
+ */
+ @InspectableProperty(
+ attributeId = com.android.internal.R.styleable.AnalogClock_hand_secondTintMode)
+ @Nullable
+ public BlendMode getSecondHandTintBlendMode() {
+ return mSecondHandTintInfo.mTintBlendMode;
+ }
+
+ /**
* Indicates which time zone is currently used by this view.
*
* @return The ID of the current time zone or null if the default time zone,
@@ -462,4 +793,36 @@
return null;
}
}
+
+ private final class TintInfo {
+ boolean mHasTintList;
+ @Nullable ColorStateList mTintList;
+ boolean mHasTintBlendMode;
+ @Nullable BlendMode mTintBlendMode;
+
+ /**
+ * Returns a mutated copy of {@code drawable} with tinting applied, or null if it's null.
+ */
+ @Nullable
+ Drawable apply(@Nullable Drawable drawable) {
+ if (drawable == null) return null;
+
+ Drawable newDrawable = drawable.mutate();
+
+ if (mHasTintList) {
+ newDrawable.setTintList(mTintList);
+ }
+
+ if (mHasTintBlendMode) {
+ newDrawable.setTintBlendMode(mTintBlendMode);
+ }
+
+ // All drawables should have the same state as the View itself.
+ if (drawable.isStateful()) {
+ newDrawable.setState(getDrawableState());
+ }
+
+ return newDrawable;
+ }
+ }
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 6dedd12..23915e0 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -303,6 +303,27 @@
}
/**
+ * Returns the {@link EdgeEffect#getType()} for the edge effects.
+ * @return the {@link EdgeEffect#getType()} for the edge effects.
+ * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+ */
+ @EdgeEffect.EdgeEffectType
+ public int getEdgeEffectType() {
+ return mEdgeGlowLeft.getType();
+ }
+
+ /**
+ * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
+ * @param type The edge effect type to use for the edge effects.
+ * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+ */
+ public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
+ mEdgeGlowRight.setType(type);
+ mEdgeGlowLeft.setType(type);
+ invalidate();
+ }
+
+ /**
* @return The maximum amount this scroll view will scroll in response to
* an arrow event.
*/
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 0a08ccd..8aa557b 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -648,6 +648,7 @@
* @see #getImageTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @android.view.RemotableViewMethod
public void setImageTintList(@Nullable ColorStateList tint) {
mDrawableTintList = tint;
mHasDrawableTint = true;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index a44808e..1b76ebf 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1308,6 +1308,7 @@
* @see #getSecondaryProgressTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setSecondaryProgressTintList(@Nullable ColorStateList tint) {
if (mProgressTintInfo == null) {
mProgressTintInfo = new ProgressTintInfo();
@@ -1619,6 +1620,7 @@
* @param stateDescription The state description.
*/
@Override
+ @RemotableViewMethod
public void setStateDescription(@Nullable CharSequence stateDescription) {
mCustomStateDescription = stateDescription;
if (stateDescription == null) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2112fb1..e0b4ec7 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -46,6 +46,8 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.loader.ResourcesLoader;
+import android.content.res.loader.ResourcesProvider;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Outline;
@@ -62,16 +64,19 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.Process;
import android.os.StrictMode;
import android.os.UserHandle;
+import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.TypedValue.ComplexDimensionUnit;
import android.view.ContextThemeWrapper;
@@ -92,6 +97,12 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.util.Preconditions;
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -211,6 +222,7 @@
private static final int SET_RADIO_GROUP_CHECKED = 27;
private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28;
private static final int SET_ON_CHECKED_CHANGE_RESPONSE_TAG = 29;
+ private static final int NIGHT_MODE_REFLECTION_ACTION_TAG = 30;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
@@ -223,35 +235,17 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface MarginType {}
- /**
- * The value will apply to the marginLeft.
- * @hide
- */
+ /** The value will apply to the marginLeft. */
public static final int MARGIN_LEFT = 0;
- /**
- * The value will apply to the marginTop.
- * @hide
- */
+ /** The value will apply to the marginTop. */
public static final int MARGIN_TOP = 1;
- /**
- * The value will apply to the marginRight.
- * @hide
- */
+ /** The value will apply to the marginRight. */
public static final int MARGIN_RIGHT = 2;
- /**
- * The value will apply to the marginBottom.
- * @hide
- */
+ /** The value will apply to the marginBottom. */
public static final int MARGIN_BOTTOM = 3;
- /**
- * The value will apply to the marginStart.
- * @hide
- */
+ /** The value will apply to the marginStart. */
public static final int MARGIN_START = 4;
- /**
- * The value will apply to the marginEnd.
- * @hide
- */
+ /** The value will apply to the marginEnd. */
public static final int MARGIN_END = 5;
/** @hide **/
@@ -537,8 +531,8 @@
* SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
*/
private abstract static class Action implements Parcelable {
- public abstract void apply(View root, ViewGroup rootParent,
- InteractionHandler handler) throws ActionException;
+ public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) throws ActionException;
public static final int MERGE_REPLACE = 0;
public static final int MERGE_APPEND = 1;
@@ -568,8 +562,8 @@
* and return the final action which will run on the UI thread.
* Override this if some of the tasks can be performed async.
*/
- public Action initActionAsync(
- ViewTree root, ViewGroup rootParent, InteractionHandler handler) {
+ public Action initActionAsync(ViewTree root, ViewGroup rootParent,
+ InteractionHandler handler, ColorResources colorResources) {
return this;
}
@@ -612,7 +606,9 @@
// Constant used during async execution. It is not parcelable.
private static final Action ACTION_NOOP = new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { }
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
+ }
};
/**
@@ -730,7 +726,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View view = root.findViewById(viewId);
if (!(view instanceof AdapterView<?>)) return;
@@ -765,7 +762,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -837,7 +835,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -860,7 +859,8 @@
if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
- v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
+ v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
+ colorResources));
}
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
@@ -868,7 +868,8 @@
if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
- v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
+ v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
+ colorResources));
}
}
}
@@ -899,7 +900,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -936,7 +938,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler) {
+ InteractionHandler handler, ColorResources colorResources) {
SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
copy.isAsync = true;
return copy;
@@ -975,7 +977,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1054,7 +1057,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) return;
if (!(target instanceof CompoundButton)) {
@@ -1257,7 +1261,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1314,7 +1319,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1351,7 +1357,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1454,12 +1461,12 @@
}
@Override
- public void apply(View root, ViewGroup rootParent,
- InteractionHandler handler) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) throws ActionException {
ReflectionAction ra = new ReflectionAction(viewId, methodName,
BaseReflectionAction.BITMAP,
bitmap);
- ra.apply(root, rootParent, handler);
+ ra.apply(root, rootParent, handler, colorResources);
}
@Override
@@ -1535,7 +1542,8 @@
protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
@Override
- public final void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public final void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1553,7 +1561,7 @@
@Override
public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler) {
+ InteractionHandler handler, ColorResources colorResources) {
final View view = root.findViewById(viewId);
if (view == null) return ACTION_NOOP;
@@ -1877,6 +1885,73 @@
}
}
+ private final class NightModeReflectionAction extends BaseReflectionAction {
+
+ private final Object mLightValue;
+ private final Object mDarkValue;
+
+ NightModeReflectionAction(
+ @IdRes int viewId,
+ String methodName,
+ int type,
+ Object lightValue,
+ Object darkValue) {
+ super(viewId, methodName, type);
+ mLightValue = lightValue;
+ mDarkValue = darkValue;
+ }
+
+ NightModeReflectionAction(Parcel in) {
+ super(in);
+ switch (this.type) {
+ case ICON:
+ mLightValue = in.readTypedObject(Icon.CREATOR);
+ mDarkValue = in.readTypedObject(Icon.CREATOR);
+ break;
+ case COLOR_STATE_LIST:
+ mLightValue = in.readTypedObject(ColorStateList.CREATOR);
+ mDarkValue = in.readTypedObject(ColorStateList.CREATOR);
+ break;
+ case INT:
+ mLightValue = in.readInt();
+ mDarkValue = in.readInt();
+ break;
+ default:
+ throw new ActionException("Unexpected night mode action type: " + this.type);
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ switch (this.type) {
+ case ICON:
+ case COLOR_STATE_LIST:
+ out.writeTypedObject((Parcelable) mLightValue, flags);
+ out.writeTypedObject((Parcelable) mDarkValue, flags);
+ break;
+ case INT:
+ out.writeInt((int) mLightValue);
+ out.writeInt((int) mDarkValue);
+ break;
+ }
+ }
+
+ @Nullable
+ @Override
+ protected Object getParameterValue(@Nullable View view) throws ActionException {
+ if (view == null) return null;
+
+ Configuration configuration = view.getResources().getConfiguration();
+ return configuration.isNightModeActive() ? mDarkValue : mLightValue;
+ }
+
+ @Override
+ public int getActionTag() {
+ return NIGHT_MODE_REFLECTION_ACTION_TAG;
+ }
+ }
+
/**
* This is only used for async execution of actions and it not parcelable.
*/
@@ -1888,7 +1963,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
mRunnable.run();
}
}
@@ -1943,7 +2019,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final Context context = root.getContext();
final ViewGroup target = root.findViewById(viewId);
@@ -1952,12 +2029,14 @@
}
// Inflate nested views and add as children
- target.addView(mNestedViews.apply(context, target, handler), mIndex);
+ target.addView(
+ mNestedViews.apply(context, target, handler, null /* size */, colorResources),
+ mIndex);
}
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler) {
+ InteractionHandler handler, ColorResources colorResources) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -1969,8 +2048,8 @@
// Inflate nested views and perform all the async tasks for the child remoteView.
final Context context = root.mRoot.getContext();
- final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(
- context, targetVg, null, handler);
+ final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(context, targetVg,
+ null /* listener */, handler, null /* size */, colorResources);
final ViewTree tree = task.doInBackground();
if (tree == null) {
@@ -1983,8 +2062,8 @@
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
- throws ActionException {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) throws ActionException {
task.onPostExecute(tree);
targetVg.addView(task.mResult, mIndex);
}
@@ -2045,7 +2124,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final ViewGroup target = root.findViewById(viewId);
if (target == null) {
@@ -2062,7 +2142,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler) {
+ InteractionHandler handler, ColorResources colorResources) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -2086,8 +2166,8 @@
}
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
- throws ActionException {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) throws ActionException {
if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
targetVg.removeAllViews();
return;
@@ -2142,7 +2222,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null || target == root) {
@@ -2157,7 +2238,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler) {
+ InteractionHandler handler, ColorResources colorResources) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the correct view.
root.createTree();
@@ -2176,8 +2257,8 @@
parent.mChildren.remove(target);
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
- throws ActionException {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) throws ActionException {
parentVg.removeView(target.mRoot);
}
};
@@ -2256,7 +2337,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
if (drawablesLoaded) {
@@ -2287,7 +2369,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler) {
+ InteractionHandler handler, ColorResources colorResources) {
final TextView target = root.findViewById(viewId);
if (target == null) return ACTION_NOOP;
@@ -2365,7 +2447,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
target.setTextSize(units, size);
@@ -2410,7 +2493,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) return;
target.setPadding(left, top, right, bottom);
@@ -2483,7 +2567,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) {
return;
@@ -2596,7 +2681,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -2631,7 +2717,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
// Let's traverse the viewtree and override all textColors!
Stack<View> viewsToProcess = new Stack<>();
viewsToProcess.add(root);
@@ -2681,7 +2768,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) {
final View target = root.findViewById(mViewId);
if (target == null) return;
@@ -2715,7 +2803,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources)
throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -2766,8 +2855,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
- throws ActionException {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -2843,8 +2932,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
- throws ActionException {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3243,6 +3332,8 @@
return new SetViewOutlinePreferredRadiusAction(parcel);
case SET_ON_CHECKED_CHANGE_RESPONSE_TAG:
return new SetOnCheckedChangeResponse(parcel);
+ case NIGHT_MODE_REFLECTION_ACTION_TAG:
+ return new NightModeReflectionAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -3932,7 +4023,6 @@
* @param viewId The id of the view to change
* @param type The margin being set e.g. {@link #MARGIN_END}
* @param dimen a dimension resource to apply to the margin, or 0 to clear the margin.
- * @hide
*/
public void setViewLayoutMarginDimen(@IdRes int viewId, @MarginType int type,
@DimenRes int dimen) {
@@ -3951,7 +4041,6 @@
* @param type The margin being set e.g. {@link #MARGIN_END}
* @param value a value for the margin the given units.
* @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
- * @hide
*/
public void setViewLayoutMargin(@IdRes int viewId, @MarginType int type, float value,
@ComplexDimensionUnit int units) {
@@ -3969,7 +4058,6 @@
*
* @param width Width of the view in the given units
* @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
- * @hide
*/
public void setViewLayoutWidth(@IdRes int viewId, float width,
@ComplexDimensionUnit int units) {
@@ -3981,7 +4069,6 @@
* the result of {@link Resources#getDimensionPixelSize(int)}.
*
* @param widthDimen the dimension resource for the view's width
- * @hide
*/
public void setViewLayoutWidthDimen(@IdRes int viewId, @DimenRes int widthDimen) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthDimen));
@@ -3998,7 +4085,6 @@
*
* @param height height of the view in the given units
* @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
- * @hide
*/
public void setViewLayoutHeight(@IdRes int viewId, float height,
@ComplexDimensionUnit int units) {
@@ -4010,7 +4096,6 @@
* the result of {@link Resources#getDimensionPixelSize(int)}.
*
* @param heightDimen a dimen resource to read the height from.
- * @hide
*/
public void setViewLayoutHeightDimen(@IdRes int viewId, @DimenRes int heightDimen) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightDimen));
@@ -4130,6 +4215,30 @@
ResourceReflectionAction.COLOR_RESOURCE, colorResource));
}
+ /**
+ * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param notNight The value to pass to the method when the view's configuration is set to
+ * {@link Configuration#UI_MODE_NIGHT_NO}
+ * @param night The value to pass to the method when the view's configuration is set to
+ * {@link Configuration#UI_MODE_NIGHT_YES}
+ */
+ public void setColorInt(
+ @IdRes int viewId,
+ @NonNull String methodName,
+ @ColorInt int notNight,
+ @ColorInt int night) {
+ addAction(
+ new NightModeReflectionAction(
+ viewId,
+ methodName,
+ BaseReflectionAction.INT,
+ notNight,
+ night));
+ }
+
/**
* Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
@@ -4137,10 +4246,9 @@
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
- *
- * @hide
*/
- public void setColorStateList(@IdRes int viewId, String methodName, ColorStateList value) {
+ public void setColorStateList(@IdRes int viewId, @NonNull String methodName,
+ @Nullable ColorStateList value) {
addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.COLOR_STATE_LIST,
value));
}
@@ -4148,6 +4256,30 @@
/**
* Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
*
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param notNight The value to pass to the method when the view's configuration is set to
+ * {@link Configuration#UI_MODE_NIGHT_NO}
+ * @param night The value to pass to the method when the view's configuration is set to
+ * {@link Configuration#UI_MODE_NIGHT_YES}
+ */
+ public void setColorStateList(
+ @IdRes int viewId,
+ @NonNull String methodName,
+ @Nullable ColorStateList notNight,
+ @Nullable ColorStateList night) {
+ addAction(
+ new NightModeReflectionAction(
+ viewId,
+ methodName,
+ BaseReflectionAction.COLOR_STATE_LIST,
+ notNight,
+ night));
+ }
+
+ /**
+ * 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.
*
* @param viewId The id of the view on which to call the method.
@@ -4356,6 +4488,30 @@
}
/**
+ * Call a method taking one Icon on a view in the layout for this RemoteViews.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param notNight The value to pass to the method when the view's configuration is set to
+ * {@link Configuration#UI_MODE_NIGHT_NO}
+ * @param night The value to pass to the method when the view's configuration is set to
+ * {@link Configuration#UI_MODE_NIGHT_YES}
+ */
+ public void setIcon(
+ @IdRes int viewId,
+ @NonNull String methodName,
+ @Nullable Icon notNight,
+ @Nullable Icon night) {
+ addAction(
+ new NightModeReflectionAction(
+ viewId,
+ methodName,
+ BaseReflectionAction.ICON,
+ notNight,
+ night));
+ }
+
+ /**
* Equivalent to calling View.setContentDescription(CharSequence).
*
* @param viewId The id of the view whose content description should change.
@@ -4545,7 +4701,7 @@
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
View result = inflateView(context, rvToApply, parent);
- rvToApply.performApply(result, parent, handler);
+ rvToApply.performApply(result, parent, handler, null);
return result;
}
@@ -4557,27 +4713,39 @@
/** @hide */
public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
- @Nullable InteractionHandler handler,
- @StyleRes int applyThemeResId,
+ @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
@Nullable PointF size) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
- View result = inflateView(context, rvToApply, parent, applyThemeResId);
- rvToApply.performApply(result, parent, handler);
+ View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
+ rvToApply.performApply(result, parent, handler, null);
+ return result;
+ }
+
+ /** @hide */
+ public View apply(Context context, ViewGroup parent, InteractionHandler handler,
+ @NonNull PointF size, @Nullable ColorResources colorResources) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+
+ View result = inflateView(context, rvToApply, parent, 0, colorResources);
+ rvToApply.performApply(result, parent, handler, colorResources);
return result;
}
private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
- return inflateView(context, rv, parent, 0);
+ return inflateView(context, rv, parent, 0, null);
}
private View inflateView(Context context, RemoteViews rv, ViewGroup parent,
- @StyleRes int applyThemeResId) {
+ @StyleRes int applyThemeResId, @Nullable ColorResources colorResources) {
// RemoteViews may be built by an application installed in another
// user. So build a context that loads resources from that user but
// still returns the current users userId so settings like data / time formats
// are loaded without requiring cross user persmissions.
final Context contextForResources = getContextForResources(context);
+ if (colorResources != null) {
+ colorResources.apply(contextForResources);
+ }
Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
// If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
@@ -4639,34 +4807,37 @@
*/
public CancellationSignal applyAsync(
Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
- return applyAsync(context, parent, executor, listener, null);
+ return applyAsync(context, parent, executor, listener, null /* handler */);
}
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent,
Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
- return applyAsync(context, parent, executor, listener, handler, null);
+ return applyAsync(context, parent, executor, listener, handler, null /* size */);
}
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent,
Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
PointF size) {
- return getAsyncApplyTask(context, parent, listener, handler, size).startTaskOnExecutor(
- executor);
+ return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */)
+ .startTaskOnExecutor(executor);
+ }
+
+ /** @hide */
+ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
+ OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+ ColorResources colorResources) {
+ return getAsyncApplyTask(context, parent, listener, handler, size, colorResources)
+ .startTaskOnExecutor(executor);
}
private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
- OnViewAppliedListener listener, InteractionHandler handler) {
- return getAsyncApplyTask(context, parent, listener, handler, null);
- }
-
- private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
- OnViewAppliedListener listener, InteractionHandler handler, PointF size) {
- return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context,
- listener,
- handler, null);
+ OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+ ColorResources colorResources) {
+ return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
+ handler, colorResources, null /* result */);
}
private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -4677,6 +4848,7 @@
final Context mContext;
final OnViewAppliedListener mListener;
final InteractionHandler mHandler;
+ final ColorResources mColorResources;
private View mResult;
private ViewTree mTree;
@@ -4685,11 +4857,12 @@
private AsyncApplyTask(
RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
- InteractionHandler handler, View result) {
+ InteractionHandler handler, ColorResources colorResources, View result) {
mRV = rv;
mParent = parent;
mContext = context;
mListener = listener;
+ mColorResources = colorResources;
mHandler = handler;
mResult = result;
@@ -4699,7 +4872,7 @@
protected ViewTree doInBackground(Void... params) {
try {
if (mResult == null) {
- mResult = inflateView(mContext, mRV, mParent);
+ mResult = inflateView(mContext, mRV, mParent, 0, mColorResources);
}
mTree = new ViewTree(mResult);
@@ -4708,7 +4881,8 @@
mActions = new Action[count];
for (int i = 0; i < count && !isCancelled(); i++) {
// TODO: check if isCancelled in nested views.
- mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
+ mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler,
+ mColorResources);
}
} else {
mActions = null;
@@ -4733,7 +4907,7 @@
InteractionHandler handler = mHandler == null
? DEFAULT_INTERACTION_HANDLER : mHandler;
for (Action a : mActions) {
- a.apply(viewTree.mRoot, mParent, handler);
+ a.apply(viewTree.mRoot, mParent, handler, mColorResources);
}
}
} catch (Exception e) {
@@ -4777,16 +4951,17 @@
* the {@link #apply(Context,ViewGroup)} call.
*/
public void reapply(Context context, View v) {
- reapply(context, v, null, null);
+ reapply(context, v, null /* handler */);
}
/** @hide */
public void reapply(Context context, View v, InteractionHandler handler) {
- reapply(context, v, handler, null);
+ reapply(context, v, handler, null /* size */, null /* colorResources */);
}
/** @hide */
- public void reapply(Context context, View v, InteractionHandler handler, PointF size) {
+ public void reapply(Context context, View v, InteractionHandler handler, PointF size,
+ ColorResources colorResources) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
// In the case that a view has this RemoteViews applied in one orientation or size, is
@@ -4800,7 +4975,7 @@
}
}
- rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
+ rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
}
/**
@@ -4816,20 +4991,21 @@
* @return CancellationSignal
* @hide
*/
- public CancellationSignal reapplyAsync(
- Context context, View v, Executor executor, OnViewAppliedListener listener) {
- return reapplyAsync(context, v, executor, listener, null, null);
+ public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
+ OnViewAppliedListener listener) {
+ return reapplyAsync(context, v, executor, listener, null);
}
/** @hide */
public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
OnViewAppliedListener listener, InteractionHandler handler) {
- return reapplyAsync(context, v, executor, listener, handler, null);
+ return reapplyAsync(context, v, executor, listener, handler, null, null);
}
/** @hide */
public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
- OnViewAppliedListener listener, InteractionHandler handler, PointF size) {
+ OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+ ColorResources colorResources) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
// In the case that a view has this RemoteViews applied in one orientation, is persisted
@@ -4843,16 +5019,18 @@
}
return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
- context, listener, handler, v).startTaskOnExecutor(executor);
+ context, listener, handler, colorResources, v).startTaskOnExecutor(
+ executor);
}
- private void performApply(View v, ViewGroup parent, InteractionHandler handler) {
+ private void performApply(View v, ViewGroup parent, InteractionHandler handler,
+ ColorResources colorResources) {
if (mActions != null) {
handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler;
final int count = mActions.size();
for (int i = 0; i < count; i++) {
Action a = mActions.get(i);
- a.apply(v, parent, handler);
+ a.apply(v, parent, handler, colorResources);
}
}
}
@@ -4893,6 +5071,122 @@
}
/**
+ * Object allowing the modification of a context to overload the system's dynamic colors.
+ *
+ * Only colors from {@link android.R.color#system_primary_0} to
+ * {@link android.R.color#system_neutral_1000} can be overloaded.
+ * @hide
+ */
+ public static final class ColorResources {
+ // Set of valid colors resources.
+ private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_primary_0;
+ private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_neutral_1000;
+ // Size, in bytes, of an entry in the array of colors in an ARSC file.
+ private static final int ARSC_ENTRY_SIZE = 16;
+
+ private ResourcesLoader mLoader;
+
+ private ColorResources(ResourcesLoader loader) {
+ mLoader = loader;
+ }
+
+ /**
+ * Apply the color resources to the given context.
+ *
+ * No resource resolution must have be done on the context given to that method.
+ */
+ public void apply(Context context) {
+ context.getResources().addLoaders(mLoader);
+ }
+
+ private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException {
+ ByteArrayOutputStream content = new ByteArrayOutputStream(2048);
+ byte[] buffer = new byte[4096];
+ while (input.available() > 0) {
+ int read = input.read(buffer);
+ content.write(buffer, 0, read);
+ }
+ return content;
+ }
+
+ /**
+ * Creates the compiled resources content from the asset stored in the APK.
+ *
+ * The asset is a compiled resource with the correct resources name and correct ids, only
+ * the values are incorrect. The last value is at the very end of the file. The resources
+ * are in an array, the array's entries are 16 bytes each. We use this to work out the
+ * location of all the positions of the various resources.
+ */
+ private static byte[] createCompiledResourcesContent(Context context,
+ SparseIntArray colorResources) throws IOException {
+ byte[] content;
+ try (InputStream input = context.getResources().openRawResource(
+ com.android.internal.R.raw.remote_views_color_resources)) {
+ ByteArrayOutputStream rawContent = readFileContent(input);
+ content = rawContent.toByteArray();
+ }
+ int valuesOffset =
+ content.length - (LAST_RESOURCE_COLOR_ID & 0xffff) * ARSC_ENTRY_SIZE - 4;
+ if (valuesOffset < 0) {
+ Log.e(LOG_TAG, "ARSC file for theme colors is invalid.");
+ return null;
+ }
+ for (int colorRes = FIRST_RESOURCE_COLOR_ID; colorRes <= LAST_RESOURCE_COLOR_ID;
+ colorRes++) {
+ // The last 2 bytes are the index in the color array.
+ int index = colorRes & 0xffff;
+ int offset = valuesOffset + index * ARSC_ENTRY_SIZE;
+ int value = colorResources.get(colorRes, context.getColor(colorRes));
+ // Write the 32 bit integer in little endian
+ for (int b = 0; b < 4; b++) {
+ content[offset + b] = (byte) (value & 0xff);
+ value >>= 8;
+ }
+ }
+ return content;
+ }
+
+ /**
+ * Adds a resource loader for theme colors to the given context.
+ *
+ * @param context Context of the view hosting the widget.
+ * @param colorMapping Mapping of resources to color values.
+ *
+ * @hide
+ */
+ public static ColorResources create(Context context, SparseIntArray colorMapping) {
+ try {
+ byte[] contentBytes = createCompiledResourcesContent(context, colorMapping);
+ if (contentBytes == null) {
+ return null;
+ }
+ FileDescriptor arscFile = null;
+ try {
+ arscFile = Os.memfd_create("remote_views_theme_colors.arsc", 0 /* flags */);
+ // Note: This must not be closed through the OutputStream.
+ try (OutputStream pipeWriter = new FileOutputStream(arscFile)) {
+ pipeWriter.write(contentBytes);
+
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(arscFile)) {
+ ResourcesLoader colorsLoader = new ResourcesLoader();
+ colorsLoader.addProvider(ResourcesProvider
+ .loadFromTable(pfd, null /* assetsProvider */));
+ return new ColorResources(colorsLoader);
+ }
+ }
+ } finally {
+ if (arscFile != null) {
+ Os.close(arscFile);
+ }
+ }
+ } catch (Exception ex) {
+ Log.e(LOG_TAG, "Failed to setup the context for theme colors", ex);
+ }
+ return null;
+ }
+ }
+
+ /**
* Returns the number of actions in this RemoteViews. Can be used as a sequence number.
*
* @hide
diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java
index b80fe48..827d033 100644
--- a/core/java/android/widget/RemoteViewsListAdapter.java
+++ b/core/java/android/widget/RemoteViewsListAdapter.java
@@ -31,12 +31,14 @@
private ArrayList<RemoteViews> mRemoteViewsList;
private ArrayList<Integer> mViewTypes = new ArrayList<Integer>();
private int mViewTypeCount;
+ private RemoteViews.ColorResources mColorResources;
public RemoteViewsListAdapter(Context context, ArrayList<RemoteViews> remoteViews,
- int viewTypeCount) {
+ int viewTypeCount, RemoteViews.ColorResources colorResources) {
mContext = context;
mRemoteViewsList = remoteViews;
mViewTypeCount = viewTypeCount;
+ mColorResources = colorResources;
init();
}
@@ -90,9 +92,10 @@
if (convertView != null && rv != null &&
convertView.getId() == rv.getLayoutId()) {
v = convertView;
- rv.reapply(mContext, v);
+ rv.reapply(mContext, v, null /* handler */, null /* size */, mColorResources);
} else {
- v = rv.apply(mContext, parent);
+ v = rv.apply(mContext, parent, null /* handler */, null /* size */,
+ mColorResources);
}
return v;
} else {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 64d09de..65f3da7 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -335,6 +335,27 @@
}
/**
+ * Returns the {@link EdgeEffect#getType()} for the edge effects.
+ * @return the {@link EdgeEffect#getType()} for the edge effects.
+ * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+ */
+ @EdgeEffect.EdgeEffectType
+ public int getEdgeEffectType() {
+ return mEdgeGlowTop.getType();
+ }
+
+ /**
+ * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
+ * @param type The edge effect type to use for the edge effects.
+ * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+ */
+ public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
+ mEdgeGlowTop.setType(type);
+ mEdgeGlowBottom.setType(type);
+ invalidate();
+ }
+
+ /**
* @return The maximum amount this scroll view will scroll in response to
* an arrow event.
*/
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0f2089a..ca0747f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4756,6 +4756,7 @@
* @see #getJustificationMode()
*/
@Layout.JustificationMode
+ @android.view.RemotableViewMethod
public void setJustificationMode(@Layout.JustificationMode int justificationMode) {
mJustificationMode = justificationMode;
if (mLayout != null) {
@@ -5232,6 +5233,7 @@
* @see android.view.Gravity
* @attr ref android.R.styleable#TextView_gravity
*/
+ @android.view.RemotableViewMethod
public void setGravity(int gravity) {
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.START;
@@ -5826,6 +5828,7 @@
*
* @attr ref android.R.styleable#TextView_lineHeight
*/
+ @android.view.RemotableViewMethod
public void setLineHeight(@Px @IntRange(from = 0) int lineHeight) {
Preconditions.checkArgumentNonnegative(lineHeight);
@@ -10277,6 +10280,7 @@
* @see #setTransformationMethod(TransformationMethod)
* @attr ref android.R.styleable#TextView_textAllCaps
*/
+ @android.view.RemotableViewMethod
public void setAllCaps(boolean allCaps) {
if (allCaps) {
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
index 2904a8c..0f2a3ca 100644
--- a/core/java/android/widget/ToastPresenter.java
+++ b/core/java/android/widget/ToastPresenter.java
@@ -184,7 +184,7 @@
mParams.y = yOffset;
mParams.horizontalMargin = horizontalMargin;
mParams.verticalMargin = verticalMargin;
- addToastView();
+ mView.setLayoutParams(mParams);
}
/**
diff --git a/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
index cb280cd..f3759e0 100644
--- a/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
@@ -18,5 +18,5 @@
// Iterface to observe op note/checks of ops
oneway interface IAppOpsNotedCallback {
- void opNoted(int op, int uid, String packageName, int flags, int mode);
+ void opNoted(int op, int uid, String packageName, String attributionTag, int flags, int mode);
}
diff --git a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl
index b0cb2a8..3a108e7 100644
--- a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl
@@ -18,5 +18,5 @@
// Iterface to observe op starts
oneway interface IAppOpsStartedCallback {
- void opStarted(int op, int uid, String packageName, int flags, int mode);
+ void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode);
}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index e82cc73..342456a 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -25,6 +25,9 @@
import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
+import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
+import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.HardwareRendererObserver;
@@ -72,6 +75,7 @@
private long mEndVsyncId = INVALID_ID;
private boolean mMetricsFinalized;
private boolean mCancelled = false;
+ private FrameTrackerListener mListener;
private static class JankInfo {
long frameVsyncId;
@@ -109,7 +113,7 @@
@NonNull SurfaceControlWrapper surfaceControlWrapper,
@NonNull ChoreographerWrapper choreographer,
@NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames,
- int traceThresholdFrameTimeMillis) {
+ int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) {
mSession = session;
mRendererWrapper = renderer;
mMetricsWrapper = metrics;
@@ -120,6 +124,7 @@
mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
mTraceThresholdMissedFrames = traceThresholdMissedFrames;
mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
+ mListener = listener;
// If the surface isn't valid yet, wait until it's created.
if (viewRootWrapper.getSurfaceControl().isValid()) {
@@ -165,11 +170,15 @@
*/
public synchronized void begin() {
mBeginVsyncId = mChoreographer.getVsyncId() + 1;
+ mSession.setTimeStamp(System.nanoTime());
Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
mRendererWrapper.addObserver(mObserver);
if (mSurfaceControl != null) {
mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
}
+ if (mListener != null) {
+ mListener.onNotifyCujEvents(mSession, ACTION_SESSION_BEGIN);
+ }
}
/**
@@ -224,7 +233,6 @@
}
private boolean isInRange(long vsyncId) {
-
// It's possible that we may miss a callback for the frame with vsyncId == mEndVsyncId.
// Because of that, we collect all frames even if they happen after the end so we eventually
// have a frame after the end with both callbacks present.
@@ -371,6 +379,9 @@
missedAppFramesCount + missedSfFramesCounts,
maxFrameTimeNanos,
missedSfFramesCounts);
+ if (mListener != null) {
+ mListener.onNotifyCujEvents(mSession, ACTION_METRICS_LOGGED);
+ }
}
if (DEBUG) {
Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName()
@@ -495,4 +506,17 @@
return mChoreographer.getVsyncId();
}
}
+
+ /**
+ * A listener that notifies cuj events.
+ */
+ public interface FrameTrackerListener {
+ /**
+ * Notify that the CUJ session was created.
+ *
+ * @param session the CUJ session
+ * @param action the specific action
+ */
+ void onNotifyCujEvents(Session session, String action);
+ }
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index cba6af9..0294ec3 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -16,6 +16,8 @@
package com.android.internal.jank;
+import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
+
import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
@@ -48,9 +50,12 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
import android.os.Build;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
+import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.util.Log;
import android.util.SparseArray;
@@ -59,6 +64,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
+import com.android.internal.jank.FrameTracker.FrameTrackerListener;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
import com.android.internal.util.PerfettoTrigger;
@@ -74,6 +80,8 @@
*/
public class InteractionJankMonitor {
private static final String TAG = InteractionJankMonitor.class.getSimpleName();
+ private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();
+
private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
private static final String SETTINGS_ENABLED_KEY = "enabled";
@@ -90,6 +98,14 @@
private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
+ public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
+ public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
+ public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
+ public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
+ public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP";
+ @VisibleForTesting
+ public static final String PROP_NOTIFY_CUJ_EVENT = "debug.notify_cuj_events";
+
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
@@ -256,15 +272,28 @@
*/
@VisibleForTesting
public FrameTracker createFrameTracker(View v, Session session) {
+ final Context c = v.getContext().getApplicationContext();
synchronized (this) {
+ boolean needListener = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
+ FrameTrackerListener eventsListener =
+ !needListener ? null : (s, act) -> notifyEvents(c, act, s);
+
return new FrameTracker(session, mWorker.getThreadHandler(),
new ThreadedRendererWrapper(v.getThreadedRenderer()),
new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(),
new ChoreographerWrapper(Choreographer.getInstance()), mMetrics,
- mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis);
+ mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener);
}
}
+ private void notifyEvents(Context context, String action, Session session) {
+ Intent intent = new Intent(action);
+ intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj()));
+ intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp());
+ intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY);
+ context.sendBroadcast(intent);
+ }
+
/**
* Begin a trace session.
*
@@ -479,6 +508,7 @@
public static class Session {
@CujType
private int mCujType;
+ private long mTimeStamp;
public Session(@CujType int cujType) {
mCujType = cujType;
@@ -505,5 +535,13 @@
public String getName() {
return "J<" + getNameOfCuj(mCujType) + ">";
}
+
+ public void setTimeStamp(long timeStamp) {
+ mTimeStamp = timeStamp;
+ }
+
+ public long getTimeStamp() {
+ return mTimeStamp;
+ }
}
}
diff --git a/core/java/com/android/internal/listeners/ListenerTransportManager.java b/core/java/com/android/internal/listeners/ListenerTransportManager.java
index 0d5d1b7b..d5b5619 100644
--- a/core/java/com/android/internal/listeners/ListenerTransportManager.java
+++ b/core/java/com/android/internal/listeners/ListenerTransportManager.java
@@ -17,6 +17,7 @@
package com.android.internal.listeners;
import android.os.RemoteException;
+import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
@@ -36,13 +37,17 @@
@GuardedBy("mRegistrations")
private final Map<Object, WeakReference<TTransport>> mRegistrations;
- protected ListenerTransportManager() {
+ protected ListenerTransportManager(boolean allowServerSideTransportRemoval) {
// using weakhashmap means that the transport may be GCed if the server drops its reference,
// and thus the listener may be GCed as well if the client drops that reference. if the
// server will never drop a reference without warning (ie, transport removal may only be
// initiated from the client side), then arraymap or similar may be used without fear of
// memory leaks.
- mRegistrations = new WeakHashMap<>();
+ if (allowServerSideTransportRemoval) {
+ mRegistrations = new WeakHashMap<>();
+ } else {
+ mRegistrations = new ArrayMap<>();
+ }
}
/**
@@ -53,16 +58,21 @@
synchronized (mRegistrations) {
// ordering of operations is important so that if an error occurs at any point we
// are left in a reasonable state
- registerTransport(transport);
- WeakReference<TTransport> oldTransportRef = mRegistrations.put(key,
- new WeakReference<>(transport));
+ TTransport oldTransport;
+ WeakReference<TTransport> oldTransportRef = mRegistrations.get(key);
if (oldTransportRef != null) {
- TTransport oldTransport = oldTransportRef.get();
- if (oldTransport != null) {
- oldTransport.unregister();
- unregisterTransport(oldTransport);
- }
+ oldTransport = oldTransportRef.get();
+ } else {
+ oldTransport = null;
}
+
+ if (oldTransport == null) {
+ registerTransport(transport);
+ } else {
+ registerTransport(transport, oldTransport);
+ oldTransport.unregister();
+ }
+ mRegistrations.put(key, new WeakReference<>(transport));
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -91,7 +101,33 @@
}
}
+ /**
+ * Registers a new transport.
+ */
protected abstract void registerTransport(TTransport transport) throws RemoteException;
+ /**
+ * Registers a new transport that is replacing the given old transport. Implementations must
+ * ensure that if they throw a remote exception, the call does not have any side effects.
+ */
+ protected void registerTransport(TTransport transport, TTransport oldTransport)
+ throws RemoteException {
+ registerTransport(transport);
+ try {
+ unregisterTransport(oldTransport);
+ } catch (RemoteException e) {
+ try {
+ // best effort to ensure there are no side effects
+ unregisterTransport(transport);
+ } catch (RemoteException suppressed) {
+ e.addSuppressed(suppressed);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Unregisters an existing transport.
+ */
protected abstract void unregisterTransport(TTransport transport) throws RemoteException;
}
diff --git a/core/java/com/android/internal/os/KernelCpuBpfTracking.java b/core/java/com/android/internal/os/KernelCpuBpfTracking.java
index 2852547..387d327 100644
--- a/core/java/com/android/internal/os/KernelCpuBpfTracking.java
+++ b/core/java/com/android/internal/os/KernelCpuBpfTracking.java
@@ -16,11 +16,79 @@
package com.android.internal.os;
-/** CPU tracking using eBPF. */
+import android.annotation.Nullable;
+
+/**
+ * CPU tracking using eBPF.
+ *
+ * The tracking state and data about available frequencies are cached to avoid JNI calls and
+ * creating temporary arrays. The data is stored in a format that is convenient for metrics
+ * computation.
+ *
+ * Synchronization is not needed because the underlying native library can be invoked concurrently
+ * and getters are idempotent.
+ */
public final class KernelCpuBpfTracking {
+ private static boolean sTracking = false;
+
+ /** Cached mapping from frequency index to frequency in kHz. */
+ private static long[] sFreqs = null;
+
+ /** Cached mapping from frequency index to CPU cluster / policy. */
+ private static int[] sFreqsClusters = null;
+
private KernelCpuBpfTracking() {
}
/** Returns whether CPU tracking using eBPF is supported. */
public static native boolean isSupported();
+
+ /** Starts CPU tracking using eBPF. */
+ public static boolean startTracking() {
+ if (!sTracking) {
+ sTracking = startTrackingInternal();
+ }
+ return sTracking;
+ }
+
+ private static native boolean startTrackingInternal();
+
+ /** Returns frequencies in kHz on which CPU is tracked. Empty if not supported. */
+ public static long[] getFreqs() {
+ if (sFreqs == null) {
+ long[] freqs = getFreqsInternal();
+ if (freqs == null) {
+ return new long[0];
+ }
+ sFreqs = freqs;
+ }
+ return sFreqs;
+ }
+
+ @Nullable
+ static native long[] getFreqsInternal();
+
+ /**
+ * Returns the cluster (policy) number for each frequency on which CPU is tracked. Empty if
+ * not supported.
+ */
+ public static int[] getFreqsClusters() {
+ if (sFreqsClusters == null) {
+ int[] freqsClusters = getFreqsClustersInternal();
+ if (freqsClusters == null) {
+ return new int[0];
+ }
+ sFreqsClusters = freqsClusters;
+ }
+ return sFreqsClusters;
+ }
+
+ @Nullable
+ private static native int[] getFreqsClustersInternal();
+
+ /** Returns the number of clusters (policies). */
+ public static int getClusters() {
+ int[] freqClusters = getFreqsClusters();
+ return freqClusters.length > 0 ? freqClusters[freqClusters.length - 1] + 1 : 0;
+ }
}
diff --git a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
index 06760e1..553eda4 100644
--- a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
+++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
@@ -16,22 +16,22 @@
package com.android.internal.os;
-/**
- * Reads total CPU time bpf map.
- */
+import android.annotation.Nullable;
+
+/** Reads total CPU time bpf map. */
public final class KernelCpuTotalBpfMapReader {
private KernelCpuTotalBpfMapReader() {
}
- /** Reads total CPU time from bpf map. */
- public static native boolean read(Callback callback);
-
- /** Callback accepting values read from bpf map. */
- public interface Callback {
- /**
- * Accepts values read from bpf map: cluster index, frequency in kilohertz and time in
- * milliseconds that the cpu cluster spent at the frequency (excluding sleep).
- */
- void accept(int cluster, int freqKhz, long timeMs);
+ /** Reads total CPU times (excluding sleep) per frequency in milliseconds from bpf map. */
+ @Nullable
+ public static long[] read() {
+ if (!KernelCpuBpfTracking.startTracking()) {
+ return null;
+ }
+ return readInternal();
}
+
+ @Nullable
+ private static native long[] readInternal();
}
diff --git a/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
index dafb924..52c0c3f 100644
--- a/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
@@ -68,14 +68,15 @@
final String mTag = this.getClass().getSimpleName();
private int mErrors = 0;
- private boolean mTracking = false;
protected SparseArray<long[]> mData = new SparseArray<>();
private long mLastReadTime = 0;
protected final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
protected final ReentrantReadWriteLock.ReadLock mReadLock = mLock.readLock();
protected final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock();
- public native boolean startTrackingBpfTimes();
+ public boolean startTrackingBpfTimes() {
+ return KernelCpuBpfTracking.startTracking();
+ }
protected abstract boolean readBpfData();
@@ -116,7 +117,7 @@
if (mErrors > ERROR_THRESHOLD) {
return null;
}
- if (!mTracking && !startTrackingBpfTimes()) {
+ if (!startTrackingBpfTimes()) {
Slog.w(mTag, "Failed to start tracking");
mErrors++;
return null;
@@ -182,7 +183,9 @@
protected final native boolean readBpfData();
@Override
- public final native long[] getDataDimensions();
+ public final long[] getDataDimensions() {
+ return KernelCpuBpfTracking.getFreqsInternal();
+ }
@Override
public void removeUidsInRange(int startUid, int endUid) {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index c8afea9..b99c953 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -103,7 +103,7 @@
*/
public static final int PROFILE_FROM_SHELL = 1 << 15;
- /**
+ /*
* Enable using the ART app image startup cache
*/
public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
@@ -116,6 +116,13 @@
*/
public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
+ /**
+ * Disable runtime access to {@link android.annotation.TestApi} annotated members.
+ *
+ * <p>This only takes effect if Hidden API access restrictions are enabled as well.
+ */
+ public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
+
public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
public static final int MEMORY_TAG_LEVEL_NONE = 0;
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 56b25b2..93ba037 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -180,6 +180,7 @@
"android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
}
+ /** Load animation by resource Id from specific LayoutParams. */
@Nullable
private Animation loadAnimationRes(LayoutParams lp, int resId) {
Context context = mContext;
@@ -193,6 +194,7 @@
return null;
}
+ /** Load animation by resource Id from specific package. */
@Nullable
private Animation loadAnimationRes(String packageName, int resId) {
if (ResourceId.isValid(resId)) {
@@ -204,6 +206,13 @@
return null;
}
+ /** Load animation by resource Id from android package. */
+ @Nullable
+ public Animation loadDefaultAnimationRes(int resId) {
+ return loadAnimationRes("android", resId);
+ }
+
+ /** Load animation by attribute Id from specific LayoutParams */
@Nullable
public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
int resId = Resources.ID_NULL;
@@ -222,6 +231,25 @@
return null;
}
+ /** Load animation by attribute Id from android package. */
+ @Nullable
+ public Animation loadDefaultAnimationAttr(int animAttr) {
+ int resId = Resources.ID_NULL;
+ Context context = mContext;
+ if (animAttr >= 0) {
+ AttributeCache.Entry ent = getCachedAnimations("android",
+ mDefaultWindowAnimationStyleResId);
+ if (ent != null) {
+ context = ent.context;
+ resId = ent.array.getResourceId(animAttr, 0);
+ }
+ }
+ if (ResourceId.isValid(resId)) {
+ return loadAnimationSafely(context, resId, mTag);
+ }
+ return null;
+ }
+
@Nullable
private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
if (mDebug) {
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index c336373..8d82e33 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -35,7 +35,8 @@
* {@hide}
*/
oneway interface IInputMethod {
- void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps);
+ void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps,
+ int configChanges);
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
in IInlineSuggestionsRequestCallback cb);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index cf8711b..dd1a594 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -175,6 +175,7 @@
"android_hardware_Camera.cpp",
"android_hardware_camera2_CameraMetadata.cpp",
"android_hardware_camera2_DngCreator.cpp",
+ "android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp",
"android_hardware_camera2_utils_SurfaceUtils.cpp",
"android_hardware_display_DisplayManagerGlobal.cpp",
"android_hardware_display_DisplayViewport.cpp",
@@ -231,6 +232,7 @@
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"av-types-aidl-cpp",
+ "android.hardware.camera.device@3.2",
"libandroidicu",
"libbpf_android",
"libnetdbpf",
@@ -260,6 +262,7 @@
"libdataloader",
"libvulkan",
"libETC1",
+ "libjpeg",
"libhardware",
"libhardware_legacy",
"libselinux",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 38bcc0f..1751be0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -74,6 +74,7 @@
extern int register_android_hardware_Camera(JNIEnv *env);
extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env);
extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
+extern int register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv* env);
extern int register_android_hardware_camera2_utils_SurfaceUtils(JNIEnv* env);
extern int register_android_hardware_display_DisplayManagerGlobal(JNIEnv* env);
extern int register_android_hardware_HardwareBuffer(JNIEnv *env);
@@ -1533,6 +1534,7 @@
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
REG_JNI(register_android_hardware_camera2_DngCreator),
+ REG_JNI(register_android_hardware_camera2_impl_CameraExtensionJpegProcessor),
REG_JNI(register_android_hardware_camera2_utils_SurfaceUtils),
REG_JNI(register_android_hardware_display_DisplayManagerGlobal),
REG_JNI(register_android_hardware_HardwareBuffer),
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 5b327d4..d0504fb 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -421,11 +421,18 @@
};
static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jlong sensorManager,
- jobject eventQWeak, jobject msgQ, jstring packageName, jint mode) {
+ jobject eventQWeak, jobject msgQ, jstring packageName,
+ jint mode, jstring opPackageName, jstring attributionTag) {
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
ScopedUtfChars packageUtf(env, packageName);
String8 clientName(packageUtf.c_str());
- sp<SensorEventQueue> queue(mgr->createEventQueue(clientName, mode));
+
+ String16 attributionTagName("");
+ if (attributionTag != nullptr) {
+ ScopedUtfChars attrUtf(env, attributionTag);
+ attributionTagName = String16(attrUtf.c_str());
+ }
+ sp<SensorEventQueue> queue(mgr->createEventQueue(clientName, mode, attributionTagName));
if (queue == NULL) {
jniThrowRuntimeException(env, "Cannot construct native SensorEventQueue.");
@@ -517,29 +524,20 @@
};
static const JNINativeMethod gBaseEventQueueMethods[] = {
- {"nativeInitBaseEventQueue",
- "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/String;)J",
- (void*)nativeInitSensorEventQueue },
+ {"nativeInitBaseEventQueue",
+ "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/"
+ "String;Ljava/lang/String;)J",
+ (void *)nativeInitSensorEventQueue},
- {"nativeEnableSensor",
- "(JIII)I",
- (void*)nativeEnableSensor },
+ {"nativeEnableSensor", "(JIII)I", (void *)nativeEnableSensor},
- {"nativeDisableSensor",
- "(JI)I",
- (void*)nativeDisableSensor },
+ {"nativeDisableSensor", "(JI)I", (void *)nativeDisableSensor},
- {"nativeDestroySensorEventQueue",
- "(J)V",
- (void*)nativeDestroySensorEventQueue },
+ {"nativeDestroySensorEventQueue", "(J)V", (void *)nativeDestroySensorEventQueue},
- {"nativeFlushSensor",
- "(J)I",
- (void*)nativeFlushSensor },
+ {"nativeFlushSensor", "(J)I", (void *)nativeFlushSensor},
- {"nativeInjectSensorData",
- "(JI[FIJ)I",
- (void*)nativeInjectSensorData },
+ {"nativeInjectSensorData", "(JI[FIJ)I", (void *)nativeInjectSensorData},
};
} //unnamed namespace
diff --git a/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp
new file mode 100644
index 0000000..1390759
--- /dev/null
+++ b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp
@@ -0,0 +1,629 @@
+/*
+ * 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.
+ */
+
+#include <array>
+#include <cstring>
+#include <cstdio>
+#include <inttypes.h>
+#include <memory.h>
+#include <vector>
+
+#include <setjmp.h>
+
+#include <android/hardware/camera/device/3.2/types.h>
+
+#include "core_jni_helpers.h"
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+
+#define CAMERA_PROCESSOR_CLASS_NAME "android/hardware/camera2/impl/CameraExtensionJpegProcessor"
+
+extern "C" {
+#include "jpeglib.h"
+}
+
+using namespace std;
+using namespace android;
+
+using android::hardware::camera::device::V3_2::CameraBlob;
+using android::hardware::camera::device::V3_2::CameraBlobId;
+
+class Transform;
+struct Plane;
+
+inline int sgn(int val) { return (0 < val) - (val < 0); }
+
+inline int min(int a, int b) { return a < b ? a : b; }
+
+inline int max(int a, int b) { return a > b ? a : b; }
+
+/**
+ * Represents a combined cropping and rotation transformation.
+ *
+ * The transformation maps the coordinates (mOrigX, mOrigY) and (mOneX, mOneY)
+ * in the input image to the origin and (mOutputWidth, mOutputHeight)
+ * respectively.
+ */
+class Transform {
+ public:
+ Transform(int origX, int origY, int oneX, int oneY);
+
+ static Transform forCropFollowedByRotation(int cropLeft, int cropTop,
+ int cropRight, int cropBottom, int rot90);
+
+ inline int getOutputWidth() const { return mOutputWidth; }
+
+ inline int getOutputHeight() const { return mOutputHeight; }
+
+ bool operator==(const Transform& other) const;
+
+ /**
+ * Transforms the input coordinates. Coordinates outside the cropped region
+ * are clamped to valid values.
+ */
+ void map(int x, int y, int* outX, int* outY) const;
+
+ private:
+ int mOutputWidth;
+ int mOutputHeight;
+
+ // The coordinates of the point to map the origin to.
+ const int mOrigX, mOrigY;
+ // The coordinates of the point to map the point (getOutputWidth(),
+ // getOutputHeight()) to.
+ const int mOneX, mOneY;
+
+ // A matrix for the rotational component.
+ int mMat00, mMat01;
+ int mMat10, mMat11;
+};
+
+/**
+ * Represents a model for accessing pixel data for a single plane of an image.
+ * Note that the actual data is not owned by this class, and the underlying
+ * data does not need to be stored in separate planes.
+ */
+struct Plane {
+ // The dimensions of this plane of the image
+ int width;
+ int height;
+
+ // A pointer to raw pixel data
+ const unsigned char* data;
+ // The difference in address between consecutive pixels in the same row
+ int pixelStride;
+ // The difference in address between the start of consecutive rows
+ int rowStride;
+};
+
+/**
+ * Provides an interface for simultaneously reading a certain number of rows of
+ * an image plane as contiguous arrays, suitable for use with libjpeg.
+ */
+template <unsigned int ROWS>
+class RowIterator {
+ public:
+ /**
+ * Creates a new RowIterator which will crop and rotate with the given
+ * transform.
+ *
+ * @param plane the plane to iterate over
+ * @param transform the transformation to map output values into the
+ * coordinate space of the plane
+ * @param rowLength the length of the rows returned via LoadAt(). If this is
+ * longer than the width of the output (after applying the transform), then
+ * the right-most value is repeated.
+ */
+ inline RowIterator(Plane plane, Transform transform, int rowLength);
+
+ /**
+ * Returns an array of pointers into consecutive rows of contiguous image
+ * data starting at y. That is, samples within each row are contiguous.
+ * However, the individual arrays pointed-to may be separate.
+ * When the end of the image is reached, the last row of the image is
+ * repeated.
+ * The returned pointers are valid until the next call to loadAt().
+ */
+ inline const std::array<unsigned char*, ROWS> loadAt(int baseY);
+
+ private:
+ Plane mPlane;
+ Transform mTransform;
+ // The length of a row, with padding to the next multiple of 64.
+ int mPaddedRowLength;
+ std::vector<unsigned char> mBuffer;
+};
+
+template <unsigned int ROWS>
+RowIterator<ROWS>::RowIterator(Plane plane, Transform transform,
+ int rowLength)
+ : mPlane(plane), mTransform(transform) {
+ mPaddedRowLength = rowLength;
+ mBuffer = std::vector<unsigned char>(rowLength * ROWS);
+}
+
+template <unsigned int ROWS>
+const std::array<unsigned char*, ROWS> RowIterator<ROWS>::loadAt(int baseY) {
+ std::array<unsigned char*, ROWS> bufPtrs;
+ for (unsigned int i = 0; i < ROWS; i++) {
+ bufPtrs[i] = &mBuffer[mPaddedRowLength * i];
+ }
+
+ if (mPlane.width == 0 || mPlane.height == 0) {
+ return bufPtrs;
+ }
+
+ for (unsigned int i = 0; i < ROWS; i++) {
+ int y = i + baseY;
+ y = min(y, mTransform.getOutputHeight() - 1);
+
+ int output_width = mPaddedRowLength;
+ output_width = min(output_width, mTransform.getOutputWidth());
+ output_width = min(output_width, mPlane.width);
+
+ // Each row in the output image will be copied into buf_ by gathering pixels
+ // along an axis-aligned line in the plane.
+ // The line is defined by (startX, startY) -> (endX, endY), computed via the
+ // current Transform.
+ int startX;
+ int startY;
+ mTransform.map(0, y, &startX, &startY);
+
+ int endX;
+ int endY;
+ mTransform.map(output_width - 1, y, &endX, &endY);
+
+ // Clamp (startX, startY) and (endX, endY) to the valid bounds of the plane.
+ startX = min(startX, mPlane.width - 1);
+ startY = min(startY, mPlane.height - 1);
+ endX = min(endX, mPlane.width - 1);
+ endY = min(endY, mPlane.height - 1);
+ startX = max(startX, 0);
+ startY = max(startY, 0);
+ endX = max(endX, 0);
+ endY = max(endY, 0);
+
+ // To reduce work inside the copy-loop, precompute the start, end, and
+ // stride relating the values to be gathered from mPlane into buf
+ // for this particular scan-line.
+ int dx = sgn(endX - startX);
+ int dy = sgn(endY - startY);
+ if (!(dx == 0 || dy == 0)) {
+ ALOGE("%s: Unexpected bounds: %dx%d %dx%d!", __FUNCTION__, startX, endX, startY, endY);
+ return bufPtrs;
+ }
+
+ // The index into mPlane.data of (startX, startY)
+ int plane_start = startX * mPlane.pixelStride + startY * mPlane.rowStride;
+ // The index into mPlane.data of (endX, endY)
+ int plane_end = endX * mPlane.pixelStride + endY * mPlane.rowStride;
+ // The stride, in terms of indices in plane_data, required to enumerate the
+ // samples between the start and end points.
+ int stride = dx * mPlane.pixelStride + dy * mPlane.rowStride;
+ // In the degenerate-case of a 1x1 plane, startX and endX are equal, so
+ // stride would be 0, resulting in an infinite-loop. To avoid this case,
+ // use a stride of at-least 1.
+ if (stride == 0) {
+ stride = 1;
+ }
+
+ int outX = 0;
+ for (int idx = plane_start; idx >= min(plane_start, plane_end) &&
+ idx <= max(plane_start, plane_end); idx += stride) {
+ bufPtrs[i][outX] = mPlane.data[idx];
+ outX++;
+ }
+
+ // Fill the remaining right-edge of the buffer by extending the last
+ // value.
+ unsigned char right_padding_value = bufPtrs[i][outX - 1];
+ for (; outX < mPaddedRowLength; outX++) {
+ bufPtrs[i][outX] = right_padding_value;
+ }
+ }
+
+ return bufPtrs;
+}
+
+template <typename T>
+void safeDelete(T& t) {
+ delete t;
+ t = nullptr;
+}
+
+template <typename T>
+void safeDeleteArray(T& t) {
+ delete[] t;
+ t = nullptr;
+}
+
+Transform::Transform(int origX, int origY, int oneX, int oneY)
+ : mOrigX(origX), mOrigY(origY), mOneX(oneX), mOneY(oneY) {
+ if (origX == oneX || origY == oneY) {
+ // Handle the degenerate case of cropping to a 0x0 rectangle.
+ mMat00 = 0;
+ mMat01 = 0;
+ mMat10 = 0;
+ mMat11 = 0;
+ return;
+ }
+
+ if (oneX > origX && oneY > origY) {
+ // 0-degree rotation
+ mMat00 = 1;
+ mMat01 = 0;
+ mMat10 = 0;
+ mMat11 = 1;
+ mOutputWidth = abs(oneX - origX);
+ mOutputHeight = abs(oneY - origY);
+ } else if (oneX < origX && oneY > origY) {
+ // 90-degree CCW rotation
+ mMat00 = 0;
+ mMat01 = -1;
+ mMat10 = 1;
+ mMat11 = 0;
+ mOutputWidth = abs(oneY - origY);
+ mOutputHeight = abs(oneX - origX);
+ } else if (oneX > origX && oneY < origY) {
+ // 270-degree CCW rotation
+ mMat00 = 0;
+ mMat01 = 1;
+ mMat10 = -1;
+ mMat11 = 0;
+ mOutputWidth = abs(oneY - origY);
+ mOutputHeight = abs(oneX - origX);;
+ } else if (oneX < origX && oneY < origY) {
+ // 180-degree CCW rotation
+ mMat00 = -1;
+ mMat01 = 0;
+ mMat10 = 0;
+ mMat11 = -1;
+ mOutputWidth = abs(oneX - origX);
+ mOutputHeight = abs(oneY - origY);
+ }
+}
+
+Transform Transform::forCropFollowedByRotation(int cropLeft, int cropTop, int cropRight,
+ int cropBottom, int rot90) {
+ // The input crop-region excludes cropRight and cropBottom, so transform the
+ // crop rect such that it defines the entire valid region of pixels
+ // inclusively.
+ cropRight -= 1;
+ cropBottom -= 1;
+
+ int cropXLow = min(cropLeft, cropRight);
+ int cropYLow = min(cropTop, cropBottom);
+ int cropXHigh = max(cropLeft, cropRight);
+ int cropYHigh = max(cropTop, cropBottom);
+ rot90 %= 4;
+ if (rot90 == 0) {
+ return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1);
+ } else if (rot90 == 1) {
+ return Transform(cropXHigh, cropYLow, cropXLow - 1, cropYHigh + 1);
+ } else if (rot90 == 2) {
+ return Transform(cropXHigh, cropYHigh, cropXLow - 1, cropYLow - 1);
+ } else if (rot90 == 3) {
+ return Transform(cropXLow, cropYHigh, cropXHigh + 1, cropYLow - 1);
+ }
+ // Impossible case.
+ return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1);
+}
+
+bool Transform::operator==(const Transform& other) const {
+ return other.mOrigX == mOrigX && //
+ other.mOrigY == mOrigY && //
+ other.mOneX == mOneX && //
+ other.mOneY == mOneY;
+}
+
+/**
+ * Transforms the input coordinates. Coordinates outside the cropped region
+ * are clamped to valid values.
+ */
+void Transform::map(int x, int y, int* outX, int* outY) const {
+ x = max(x, 0);
+ y = max(y, 0);
+ x = min(x, getOutputWidth() - 1);
+ y = min(y, getOutputHeight() - 1);
+ *outX = x * mMat00 + y * mMat01 + mOrigX;
+ *outY = x * mMat10 + y * mMat11 + mOrigY;
+}
+
+int compress(int img_width, int img_height, RowIterator<16>& y_row_generator,
+ RowIterator<8>& cb_row_generator, RowIterator<8>& cr_row_generator,
+ unsigned char* out_buf, size_t out_buf_capacity, std::function<void(size_t)> flush,
+ int quality) {
+ // libjpeg requires the use of setjmp/longjmp to recover from errors. Since
+ // this doesn't play well with RAII, we must use pointers and manually call
+ // delete. See POSIX documentation for longjmp() for details on why the
+ // volatile keyword is necessary.
+ volatile jpeg_compress_struct cinfov;
+
+ jpeg_compress_struct& cinfo =
+ *const_cast<struct jpeg_compress_struct*>(&cinfov);
+
+ JSAMPROW* volatile yArr = nullptr;
+ JSAMPROW* volatile cbArr = nullptr;
+ JSAMPROW* volatile crArr = nullptr;
+
+ JSAMPARRAY imgArr[3];
+
+ // Error handling
+
+ struct my_error_mgr {
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+ } err;
+
+ cinfo.err = jpeg_std_error(&err.pub);
+
+ // Default error_exit will call exit(), so override
+ // to return control via setjmp/longjmp.
+ err.pub.error_exit = [](j_common_ptr cinfo) {
+ my_error_mgr* myerr = reinterpret_cast<my_error_mgr*>(cinfo->err);
+
+ (*cinfo->err->output_message)(cinfo);
+
+ // Return control to the setjmp point (see call to setjmp()).
+ longjmp(myerr->setjmp_buffer, 1);
+ };
+
+ cinfo.err = (struct jpeg_error_mgr*)&err;
+
+ // Set the setjmp point to return to in case of error.
+ if (setjmp(err.setjmp_buffer)) {
+ // If libjpeg hits an error, control will jump to this point (see call to
+ // longjmp()).
+ jpeg_destroy_compress(&cinfo);
+
+ safeDeleteArray(yArr);
+ safeDeleteArray(cbArr);
+ safeDeleteArray(crArr);
+
+ return -1;
+ }
+
+ // Create jpeg compression context
+ jpeg_create_compress(&cinfo);
+
+ // Stores data needed by our c-style callbacks into libjpeg
+ struct ClientData {
+ unsigned char* out_buf;
+ size_t out_buf_capacity;
+ std::function<void(size_t)> flush;
+ int totalOutputBytes;
+ } clientData{out_buf, out_buf_capacity, flush, 0};
+
+ cinfo.client_data = &clientData;
+
+ // Initialize destination manager
+ jpeg_destination_mgr dest;
+
+ dest.init_destination = [](j_compress_ptr cinfo) {
+ ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
+
+ cinfo->dest->next_output_byte = cdata.out_buf;
+ cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
+ };
+
+ dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean {
+ ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
+
+ size_t numBytesInBuffer = cdata.out_buf_capacity;
+ cdata.flush(numBytesInBuffer);
+ cdata.totalOutputBytes += numBytesInBuffer;
+
+ // Reset the buffer
+ cinfo->dest->next_output_byte = cdata.out_buf;
+ cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
+
+ return true;
+ };
+
+ dest.term_destination = [](j_compress_ptr cinfo __unused) {
+ // do nothing to terminate the output buffer
+ };
+
+ cinfo.dest = &dest;
+
+ // Set jpeg parameters
+ cinfo.image_width = img_width;
+ cinfo.image_height = img_height;
+ cinfo.input_components = 3;
+
+ // Set defaults based on the above values
+ jpeg_set_defaults(&cinfo);
+
+ jpeg_set_quality(&cinfo, quality, true);
+
+ cinfo.dct_method = JDCT_IFAST;
+
+ cinfo.raw_data_in = true;
+
+ jpeg_set_colorspace(&cinfo, JCS_YCbCr);
+
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+
+ jpeg_start_compress(&cinfo, true);
+
+ yArr = new JSAMPROW[cinfo.comp_info[0].v_samp_factor * DCTSIZE];
+ cbArr = new JSAMPROW[cinfo.comp_info[1].v_samp_factor * DCTSIZE];
+ crArr = new JSAMPROW[cinfo.comp_info[2].v_samp_factor * DCTSIZE];
+
+ imgArr[0] = const_cast<JSAMPARRAY>(yArr);
+ imgArr[1] = const_cast<JSAMPARRAY>(cbArr);
+ imgArr[2] = const_cast<JSAMPARRAY>(crArr);
+
+ for (int y = 0; y < img_height; y += DCTSIZE * 2) {
+ std::array<unsigned char*, 16> yData = y_row_generator.loadAt(y);
+ std::array<unsigned char*, 8> cbData = cb_row_generator.loadAt(y / 2);
+ std::array<unsigned char*, 8> crData = cr_row_generator.loadAt(y / 2);
+
+ for (int row = 0; row < DCTSIZE * 2; row++) {
+ yArr[row] = yData[row];
+ }
+ for (int row = 0; row < DCTSIZE; row++) {
+ cbArr[row] = cbData[row];
+ crArr[row] = crData[row];
+ }
+
+ jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2);
+ }
+
+ jpeg_finish_compress(&cinfo);
+
+ int numBytesInBuffer = cinfo.dest->next_output_byte - out_buf;
+
+ flush(numBytesInBuffer);
+
+ clientData.totalOutputBytes += numBytesInBuffer;
+
+ safeDeleteArray(yArr);
+ safeDeleteArray(cbArr);
+ safeDeleteArray(crArr);
+
+ jpeg_destroy_compress(&cinfo);
+
+ return clientData.totalOutputBytes;
+}
+
+int compress(
+ /** Input image dimensions */
+ int width, int height,
+ /** Y Plane */
+ unsigned char* yBuf, int yPStride, int yRStride,
+ /** Cb Plane */
+ unsigned char* cbBuf, int cbPStride, int cbRStride,
+ /** Cr Plane */
+ unsigned char* crBuf, int crPStride, int crRStride,
+ /** Output */
+ unsigned char* outBuf, size_t outBufCapacity,
+ /** Jpeg compression parameters */
+ int quality,
+ /** Crop */
+ int cropLeft, int cropTop, int cropRight, int cropBottom,
+ /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree
+ * rotation. */
+ int rot90) {
+ int finalWidth;
+ int finalHeight;
+ finalWidth = cropRight - cropLeft;
+ finalHeight = cropBottom - cropTop;
+
+ rot90 %= 4;
+ // for 90 and 270-degree rotations, flip the final width and height
+ if (rot90 == 1) {
+ finalWidth = cropBottom - cropTop;
+ finalHeight = cropRight - cropLeft;
+ } else if (rot90 == 3) {
+ finalWidth = cropBottom - cropTop;
+ finalHeight = cropRight - cropLeft;
+ }
+
+ const Plane yP = {width, height, yBuf, yPStride, yRStride};
+ const Plane cbP = {width / 2, height / 2, cbBuf, cbPStride, cbRStride};
+ const Plane crP = {width / 2, height / 2, crBuf, crPStride, crRStride};
+
+ auto flush = [](size_t numBytes __unused) {
+ // do nothing
+ };
+
+ // Round up to the nearest multiple of 64.
+ int y_row_length = (finalWidth + 16 + 63) & ~63;
+ int cb_row_length = (finalWidth / 2 + 16 + 63) & ~63;
+ int cr_row_length = (finalWidth / 2 + 16 + 63) & ~63;
+
+ Transform yTrans = Transform::forCropFollowedByRotation(
+ cropLeft, cropTop, cropRight, cropBottom, rot90);
+
+ Transform chromaTrans = Transform::forCropFollowedByRotation(
+ cropLeft / 2, cropTop / 2, cropRight / 2, cropBottom / 2, rot90);
+
+ RowIterator<16> yIter(yP, yTrans, y_row_length);
+ RowIterator<8> cbIter(cbP, chromaTrans, cb_row_length);
+ RowIterator<8> crIter(crP, chromaTrans, cr_row_length);
+
+ return compress(finalWidth, finalHeight, yIter, cbIter, crIter, outBuf, outBufCapacity, flush,
+ quality);
+}
+
+extern "C" {
+
+static jint CameraExtensionJpegProcessor_compressJpegFromYUV420p(
+ JNIEnv* env, jclass clazz __unused,
+ /** Input image dimensions */
+ jint width, jint height,
+ /** Y Plane */
+ jobject yBuf, jint yPStride, jint yRStride,
+ /** Cb Plane */
+ jobject cbBuf, jint cbPStride, jint cbRStride,
+ /** Cr Plane */
+ jobject crBuf, jint crPStride, jint crRStride,
+ /** Output */
+ jobject outBuf, jint outBufCapacity,
+ /** Jpeg compression parameters */
+ jint quality,
+ /** Crop */
+ jint cropLeft, jint cropTop, jint cropRight, jint cropBottom,
+ /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree
+ * rotation. */
+ jint rot90) {
+ jbyte* y = (jbyte*)env->GetDirectBufferAddress(yBuf);
+ jbyte* cb = (jbyte*)env->GetDirectBufferAddress(cbBuf);
+ jbyte* cr = (jbyte*)env->GetDirectBufferAddress(crBuf);
+ jbyte* out = (jbyte*)env->GetDirectBufferAddress(outBuf);
+
+ size_t actualJpegSize = compress(width, height,
+ (unsigned char*)y, yPStride, yRStride,
+ (unsigned char*)cb, cbPStride, cbRStride,
+ (unsigned char*)cr, crPStride, crRStride,
+ (unsigned char*)out, (size_t)outBufCapacity,
+ quality, cropLeft, cropTop, cropRight, cropBottom, rot90);
+
+ size_t finalJpegSize = actualJpegSize + sizeof(CameraBlob);
+ if (finalJpegSize > outBufCapacity) {
+ ALOGE("%s: Final jpeg buffer %zu not large enough for the jpeg blob header with "\
+ "capacity %d", __FUNCTION__, finalJpegSize, outBufCapacity);
+ return actualJpegSize;
+ }
+
+ int8_t* header = static_cast<int8_t *> (out) +
+ (outBufCapacity - sizeof(CameraBlob));
+ CameraBlob *blob = reinterpret_cast<CameraBlob *> (header);
+ blob->blobId = CameraBlobId::JPEG;
+ blob->blobSize = actualJpegSize;
+
+ return actualJpegSize;
+}
+
+} // extern "C"
+
+static const JNINativeMethod gCameraExtensionJpegProcessorMethods[] = {
+ {"compressJpegFromYUV420pNative",
+ "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIIIIII)I",
+ (void*)CameraExtensionJpegProcessor_compressJpegFromYUV420p}};
+
+// Get all the required offsets in java class and register native functions
+int register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv* env) {
+ // Register native functions
+ return RegisterMethodsOrDie(env, CAMERA_PROCESSOR_CLASS_NAME,
+ gCameraExtensionJpegProcessorMethods, NELEM(gCameraExtensionJpegProcessorMethods));
+}
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 065c79b..da60a75 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1419,6 +1419,28 @@
return nativeToJavaStatus(status);
}
+static void android_media_AudioTrack_setLogSessionId(JNIEnv *env, jobject thiz,
+ jstring jlogSessionId) {
+ sp<AudioTrack> track = getAudioTrack(env, thiz);
+ if (track == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioTrack pointer for setLogSessionId()");
+ }
+ ScopedUtfChars logSessionId(env, jlogSessionId);
+ ALOGV("%s: logSessionId %s", __func__, logSessionId.c_str());
+ track->setLogSessionId(logSessionId.c_str());
+}
+
+static void android_media_AudioTrack_setPlayerIId(JNIEnv *env, jobject thiz, jint playerIId) {
+ sp<AudioTrack> track = getAudioTrack(env, thiz);
+ if (track == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioTrack pointer for setPlayerIId()");
+ }
+ ALOGV("%s: playerIId %d", __func__, playerIId);
+ track->setPlayerIId(playerIId);
+}
+
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1496,6 +1518,9 @@
(void *)android_media_AudioTrack_getAudioDescriptionMixLeveldB},
{"native_set_dual_mono_mode", "(I)I", (void *)android_media_AudioTrack_setDualMonoMode},
{"native_get_dual_mono_mode", "([I)I", (void *)android_media_AudioTrack_getDualMonoMode},
+ {"native_setLogSessionId", "(Ljava/lang/String;)V",
+ (void *)android_media_AudioTrack_setLogSessionId},
+ {"native_setPlayerIId", "(I)V", (void *)android_media_AudioTrack_setPlayerIId},
};
// field names found in android/media/AudioTrack.java
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 7a3366a..451ea93 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -44,14 +44,15 @@
#include <ui/BlurRegion.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DeviceProductInfo.h>
-#include <ui/DisplayInfo.h>
#include <ui/DisplayMode.h>
#include <ui/DisplayedFrameStats.h>
+#include <ui/DynamicDisplayInfo.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/StaticDisplayInfo.h>
#include <utils/LightRefBase.h>
#include <utils/Log.h>
@@ -86,11 +87,24 @@
jfieldID density;
jfieldID secure;
jfieldID deviceProductInfo;
-} gDisplayInfoClassInfo;
+} gStaticDisplayInfoClassInfo;
static struct {
jclass clazz;
jmethodID ctor;
+ jfieldID supportedDisplayModes;
+ jfieldID activeDisplayModeId;
+ jfieldID supportedColorModes;
+ jfieldID activeColorMode;
+ jfieldID hdrCapabilities;
+ jfieldID autoLowLatencyModeSupported;
+ jfieldID gameContentTypeSupported;
+} gDynamicDisplayInfoClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+ jfieldID id;
jfieldID width;
jfieldID height;
jfieldID xDpi;
@@ -580,6 +594,15 @@
transaction->setBlurRegions(ctrl, blurRegionVector);
}
+static void nativeSetStretchEffect(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jfloat left, jfloat top, jfloat right,
+ jfloat bottom, jfloat vecX, jfloat vecY,
+ jfloat maxStretchAmount) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setStretchEffect(ctrl, left, top, right, bottom, vecX, vecY, maxStretchAmount);
+}
+
static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint w, jint h) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1011,51 +1034,105 @@
relativeAddress);
}
-static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) {
- DisplayInfo info;
+static jobject nativeGetStaticDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) {
+ ui::StaticDisplayInfo info;
if (const auto token = ibinderForJavaObject(env, tokenObj);
- !token || SurfaceComposerClient::getDisplayInfo(token, &info) != NO_ERROR) {
+ !token || SurfaceComposerClient::getStaticDisplayInfo(token, &info) != NO_ERROR) {
return nullptr;
}
- jobject object = env->NewObject(gDisplayInfoClassInfo.clazz, gDisplayInfoClassInfo.ctor);
- env->SetBooleanField(object, gDisplayInfoClassInfo.isInternal,
- info.connectionType == DisplayConnectionType::Internal);
- env->SetFloatField(object, gDisplayInfoClassInfo.density, info.density);
- env->SetBooleanField(object, gDisplayInfoClassInfo.secure, info.secure);
- env->SetObjectField(object, gDisplayInfoClassInfo.deviceProductInfo,
+ jobject object =
+ env->NewObject(gStaticDisplayInfoClassInfo.clazz, gStaticDisplayInfoClassInfo.ctor);
+ env->SetBooleanField(object, gStaticDisplayInfoClassInfo.isInternal,
+ info.connectionType == ui::DisplayConnectionType::Internal);
+ env->SetFloatField(object, gStaticDisplayInfoClassInfo.density, info.density);
+ env->SetBooleanField(object, gStaticDisplayInfoClassInfo.secure, info.secure);
+ env->SetObjectField(object, gStaticDisplayInfoClassInfo.deviceProductInfo,
convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo));
return object;
}
-static jobjectArray nativeGetDisplayModes(JNIEnv* env, jclass clazz, jobject tokenObj) {
- Vector<ui::DisplayMode> modes;
- if (const auto token = ibinderForJavaObject(env, tokenObj); !token ||
- SurfaceComposerClient::getDisplayModes(token, &modes) != NO_ERROR || modes.isEmpty()) {
+static jobject convertDisplayModeToJavaObject(JNIEnv* env, const ui::DisplayMode& config) {
+ jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor);
+ env->SetIntField(object, gDisplayModeClassInfo.id, config.id);
+ env->SetIntField(object, gDisplayModeClassInfo.width, config.resolution.getWidth());
+ env->SetIntField(object, gDisplayModeClassInfo.height, config.resolution.getHeight());
+ env->SetFloatField(object, gDisplayModeClassInfo.xDpi, config.xDpi);
+ env->SetFloatField(object, gDisplayModeClassInfo.yDpi, config.yDpi);
+
+ env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, config.refreshRate);
+ env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, config.appVsyncOffset);
+ env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos,
+ config.presentationDeadline);
+ env->SetIntField(object, gDisplayModeClassInfo.group, config.group);
+ return object;
+}
+
+jobject convertDeviceProductInfoToJavaObject(JNIEnv* env, const HdrCapabilities& capabilities) {
+ const auto& types = capabilities.getSupportedHdrTypes();
+ std::vector<int32_t> intTypes;
+ for (auto type : types) {
+ intTypes.push_back(static_cast<int32_t>(type));
+ }
+ auto typesArray = env->NewIntArray(types.size());
+ env->SetIntArrayRegion(typesArray, 0, intTypes.size(), intTypes.data());
+
+ return env->NewObject(gHdrCapabilitiesClassInfo.clazz, gHdrCapabilitiesClassInfo.ctor,
+ typesArray, capabilities.getDesiredMaxLuminance(),
+ capabilities.getDesiredMaxAverageLuminance(),
+ capabilities.getDesiredMinLuminance());
+}
+
+static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) {
+ ui::DynamicDisplayInfo info;
+ if (const auto token = ibinderForJavaObject(env, tokenObj);
+ !token || SurfaceComposerClient::getDynamicDisplayInfo(token, &info) != NO_ERROR) {
return nullptr;
}
- jobjectArray modesArray =
- env->NewObjectArray(modes.size(), gDisplayModeClassInfo.clazz, nullptr);
-
- for (size_t c = 0; c < modes.size(); ++c) {
- const ui::DisplayMode& mode = modes[c];
- jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor);
- env->SetIntField(object, gDisplayModeClassInfo.width, mode.resolution.getWidth());
- env->SetIntField(object, gDisplayModeClassInfo.height, mode.resolution.getHeight());
- env->SetFloatField(object, gDisplayModeClassInfo.xDpi, mode.xDpi);
- env->SetFloatField(object, gDisplayModeClassInfo.yDpi, mode.yDpi);
-
- env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, mode.refreshRate);
- env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, mode.appVsyncOffset);
- env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos,
- mode.presentationDeadline);
- env->SetIntField(object, gDisplayModeClassInfo.group, mode.group);
- env->SetObjectArrayElement(modesArray, static_cast<jsize>(c), object);
- env->DeleteLocalRef(object);
+ jobject object =
+ env->NewObject(gDynamicDisplayInfoClassInfo.clazz, gDynamicDisplayInfoClassInfo.ctor);
+ if (object == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
}
- return modesArray;
+ const auto numModes = info.supportedDisplayModes.size();
+ jobjectArray modesArray = env->NewObjectArray(numModes, gDisplayModeClassInfo.clazz, nullptr);
+ for (size_t i = 0; i < numModes; i++) {
+ const ui::DisplayMode& mode = info.supportedDisplayModes[i];
+ jobject displayModeObj = convertDisplayModeToJavaObject(env, mode);
+ env->SetObjectArrayElement(modesArray, static_cast<jsize>(i), displayModeObj);
+ env->DeleteLocalRef(displayModeObj);
+ }
+ env->SetObjectField(object, gDynamicDisplayInfoClassInfo.supportedDisplayModes, modesArray);
+ env->SetIntField(object, gDynamicDisplayInfoClassInfo.activeDisplayModeId,
+ info.activeDisplayModeId);
+
+ jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size());
+ if (colorModesArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+ jint* colorModesArrayValues = env->GetIntArrayElements(colorModesArray, 0);
+ for (size_t i = 0; i < info.supportedColorModes.size(); i++) {
+ colorModesArrayValues[i] = static_cast<jint>(info.supportedColorModes[i]);
+ }
+ env->ReleaseIntArrayElements(colorModesArray, colorModesArrayValues, 0);
+ env->SetObjectField(object, gDynamicDisplayInfoClassInfo.supportedColorModes, colorModesArray);
+
+ env->SetIntField(object, gDynamicDisplayInfoClassInfo.activeColorMode,
+ static_cast<jint>(info.activeColorMode));
+
+ env->SetObjectField(object, gDynamicDisplayInfoClassInfo.hdrCapabilities,
+ convertDeviceProductInfoToJavaObject(env, info.hdrCapabilities));
+
+ env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.autoLowLatencyModeSupported,
+ info.autoLowLatencyModeSupported);
+
+ env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.gameContentTypeSupported,
+ info.gameContentTypeSupported);
+ return object;
}
static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
@@ -1063,9 +1140,8 @@
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return JNI_FALSE;
- size_t defaultMode =
- static_cast<size_t>(env->GetIntField(DesiredDisplayModeSpecs,
- gDesiredDisplayModeSpecsClassInfo.defaultMode));
+ ui::DisplayModeId defaultMode = env->GetIntField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.defaultMode);
jboolean allowGroupSwitching =
env->GetBooleanField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching);
@@ -1095,7 +1171,7 @@
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return nullptr;
- size_t defaultMode;
+ ui::DisplayModeId defaultMode;
bool allowGroupSwitching;
float primaryRefreshRateMin;
float primaryRefreshRateMax;
@@ -1115,34 +1191,6 @@
appRequestRefreshRateMax);
}
-static jint nativeGetActiveDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return -1;
- return static_cast<jint>(SurfaceComposerClient::getActiveDisplayModeId(token));
-}
-
-static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return NULL;
- Vector<ui::ColorMode> colorModes;
- if (SurfaceComposerClient::getDisplayColorModes(token, &colorModes) != NO_ERROR ||
- colorModes.isEmpty()) {
- return NULL;
- }
-
- jintArray colorModesArray = env->NewIntArray(colorModes.size());
- if (colorModesArray == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- return NULL;
- }
- jint* colorModesArrayValues = env->GetIntArrayElements(colorModesArray, 0);
- for (size_t i = 0; i < colorModes.size(); i++) {
- colorModesArrayValues[i] = static_cast<jint>(colorModes[i]);
- }
- env->ReleaseIntArrayElements(colorModesArray, colorModesArrayValues, 0);
- return colorModesArray;
-}
-
static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return NULL;
@@ -1203,12 +1251,6 @@
return jprimaries;
}
-static jint nativeGetActiveColorMode(JNIEnv* env, jclass, jobject tokenObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return -1;
- return static_cast<jint>(SurfaceComposerClient::getActiveColorMode(token));
-}
-
static jintArray nativeGetCompositionDataspaces(JNIEnv* env, jclass) {
ui::Dataspace defaultDataspace, wcgDataspace;
ui::PixelFormat defaultPixelFormat, wcgPixelFormat;
@@ -1414,40 +1456,6 @@
transaction->reparent(ctrl, newParent);
}
-static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject tokenObject) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
- if (token == NULL) return NULL;
-
- HdrCapabilities capabilities;
- SurfaceComposerClient::getHdrCapabilities(token, &capabilities);
-
- const auto& types = capabilities.getSupportedHdrTypes();
- std::vector<int32_t> intTypes;
- for (auto type : types) {
- intTypes.push_back(static_cast<int32_t>(type));
- }
- auto typesArray = env->NewIntArray(types.size());
- env->SetIntArrayRegion(typesArray, 0, intTypes.size(), intTypes.data());
-
- return env->NewObject(gHdrCapabilitiesClassInfo.clazz, gHdrCapabilitiesClassInfo.ctor,
- typesArray, capabilities.getDesiredMaxLuminance(),
- capabilities.getDesiredMaxAverageLuminance(), capabilities.getDesiredMinLuminance());
-}
-
-static jboolean nativeGetAutoLowLatencyModeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
- if (token == NULL) return NULL;
-
- return SurfaceComposerClient::getAutoLowLatencyModeSupport(token);
-}
-
-static jboolean nativeGetGameContentTypeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
- if (token == NULL) return NULL;
-
- return SurfaceComposerClient::getGameContentTypeSupport(token);
-}
-
static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
if (token == NULL) return;
@@ -1754,6 +1762,8 @@
(void*)nativeSetLayerStack },
{"nativeSetBlurRegions", "(JJ[[FI)V",
(void*)nativeSetBlurRegions },
+ {"nativeSetStretchEffect", "(JJFFFFFFF)V",
+ (void*) nativeSetStretchEffect },
{"nativeSetShadowRadius", "(JJF)V",
(void*)nativeSetShadowRadius },
{"nativeSetFrameRate", "(JJFIZ)V",
@@ -1778,38 +1788,29 @@
(void*)nativeSetDisplayProjection },
{"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V",
(void*)nativeSetDisplaySize },
- {"nativeGetDisplayInfo", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayInfo;",
- (void*)nativeGetDisplayInfo },
- {"nativeGetDisplayModes", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayMode;",
- (void*)nativeGetDisplayModes },
- {"nativeGetActiveDisplayMode", "(Landroid/os/IBinder;)I",
- (void*)nativeGetActiveDisplayMode },
+ {"nativeGetStaticDisplayInfo",
+ "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$StaticDisplayInfo;",
+ (void*)nativeGetStaticDisplayInfo },
+ {"nativeGetDynamicDisplayInfo",
+ "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DynamicDisplayInfo;",
+ (void*)nativeGetDynamicDisplayInfo },
{"nativeSetDesiredDisplayModeSpecs",
"(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;)Z",
(void*)nativeSetDesiredDisplayModeSpecs },
{"nativeGetDesiredDisplayModeSpecs",
"(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;",
(void*)nativeGetDesiredDisplayModeSpecs },
- {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
- (void*)nativeGetDisplayColorModes},
- {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
+ {"nativeGetDisplayNativePrimaries",
+ "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
(void*)nativeGetDisplayNativePrimaries },
- {"nativeGetActiveColorMode", "(Landroid/os/IBinder;)I",
- (void*)nativeGetActiveColorMode},
{"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z",
(void*)nativeSetActiveColorMode},
- {"nativeGetAutoLowLatencyModeSupport", "(Landroid/os/IBinder;)Z",
- (void*)nativeGetAutoLowLatencyModeSupport },
{"nativeSetAutoLowLatencyMode", "(Landroid/os/IBinder;Z)V",
(void*)nativeSetAutoLowLatencyMode },
- {"nativeGetGameContentTypeSupport", "(Landroid/os/IBinder;)Z",
- (void*)nativeGetGameContentTypeSupport },
{"nativeSetGameContentType", "(Landroid/os/IBinder;Z)V",
(void*)nativeSetGameContentType },
{"nativeGetCompositionDataspaces", "()[I",
(void*)nativeGetCompositionDataspaces},
- {"nativeGetHdrCapabilities", "(Landroid/os/IBinder;)Landroid/view/Display$HdrCapabilities;",
- (void*)nativeGetHdrCapabilities },
{"nativeClearContentFrameStats", "(J)Z",
(void*)nativeClearContentFrameStats },
{"nativeGetContentFrameStats", "(JLandroid/view/WindowContentFrameStats;)Z",
@@ -1888,19 +1889,40 @@
gIntegerClassInfo.clazz = MakeGlobalRefOrDie(env, integerClass);
gIntegerClassInfo.ctor = GetMethodIDOrDie(env, gIntegerClassInfo.clazz, "<init>", "(I)V");
- jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayInfo");
- gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz);
- gDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V");
- gDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z");
- gDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F");
- gDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z");
- gDisplayInfoClassInfo.deviceProductInfo =
+ jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$StaticDisplayInfo");
+ gStaticDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz);
+ gStaticDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V");
+ gStaticDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z");
+ gStaticDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F");
+ gStaticDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z");
+ gStaticDisplayInfoClassInfo.deviceProductInfo =
GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
"Landroid/hardware/display/DeviceProductInfo;");
+ jclass dynamicInfoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DynamicDisplayInfo");
+ gDynamicDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, dynamicInfoClazz);
+ gDynamicDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, dynamicInfoClazz, "<init>", "()V");
+ gDynamicDisplayInfoClassInfo.supportedDisplayModes =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "supportedDisplayModes",
+ "[Landroid/view/SurfaceControl$DisplayMode;");
+ gDynamicDisplayInfoClassInfo.activeDisplayModeId =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "activeDisplayModeId", "I");
+ gDynamicDisplayInfoClassInfo.supportedColorModes =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I");
+ gDynamicDisplayInfoClassInfo.activeColorMode =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "activeColorMode", "I");
+ gDynamicDisplayInfoClassInfo.hdrCapabilities =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "hdrCapabilities",
+ "Landroid/view/Display$HdrCapabilities;");
+ gDynamicDisplayInfoClassInfo.autoLowLatencyModeSupported =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "autoLowLatencyModeSupported", "Z");
+ gDynamicDisplayInfoClassInfo.gameContentTypeSupported =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "gameContentTypeSupported", "Z");
+
jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode");
gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz);
gDisplayModeClassInfo.ctor = GetMethodIDOrDie(env, modeClazz, "<init>", "()V");
+ gDisplayModeClassInfo.id = GetFieldIDOrDie(env, modeClazz, "id", "I");
gDisplayModeClassInfo.width = GetFieldIDOrDie(env, modeClazz, "width", "I");
gDisplayModeClassInfo.height = GetFieldIDOrDie(env, modeClazz, "height", "I");
gDisplayModeClassInfo.xDpi = GetFieldIDOrDie(env, modeClazz, "xDpi", "F");
diff --git a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
index e6a82f6..6b41b2e 100644
--- a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
@@ -24,8 +24,48 @@
return android::bpf::isTrackingUidTimesSupported() ? JNI_TRUE : JNI_FALSE;
}
+static jboolean KernelCpuBpfTracking_startTrackingInternal(JNIEnv *, jobject) {
+ return android::bpf::startTrackingUidTimes();
+}
+
+static jlongArray KernelCpuBpfTracking_getFreqsInternal(JNIEnv *env, jobject) {
+ auto freqs = android::bpf::getCpuFreqs();
+ if (!freqs) return NULL;
+
+ std::vector<uint64_t> allFreqs;
+ for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));
+
+ auto ar = env->NewLongArray(allFreqs.size());
+ if (ar != NULL) {
+ env->SetLongArrayRegion(ar, 0, allFreqs.size(),
+ reinterpret_cast<const jlong *>(allFreqs.data()));
+ }
+ return ar;
+}
+
+static jintArray KernelCpuBpfTracking_getFreqsClustersInternal(JNIEnv *env, jobject) {
+ auto freqs = android::bpf::getCpuFreqs();
+ if (!freqs) return NULL;
+
+ std::vector<uint32_t> freqsClusters;
+ uint32_t clusters = freqs->size();
+ for (uint32_t c = 0; c < clusters; ++c) {
+ freqsClusters.insert(freqsClusters.end(), (*freqs)[c].size(), c);
+ }
+
+ auto ar = env->NewIntArray(freqsClusters.size());
+ if (ar != NULL) {
+ env->SetIntArrayRegion(ar, 0, freqsClusters.size(),
+ reinterpret_cast<const jint *>(freqsClusters.data()));
+ }
+ return ar;
+}
+
static const JNINativeMethod methods[] = {
{"isSupported", "()Z", (void *)KernelCpuBpfTracking_isSupported},
+ {"startTrackingInternal", "()Z", (void *)KernelCpuBpfTracking_startTrackingInternal},
+ {"getFreqsInternal", "()[J", (void *)KernelCpuBpfTracking_getFreqsInternal},
+ {"getFreqsClustersInternal", "()[I", (void *)KernelCpuBpfTracking_getFreqsClustersInternal},
};
int register_com_android_internal_os_KernelCpuBpfTracking(JNIEnv *env) {
diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
index 7249238..ad43014 100644
--- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
@@ -20,33 +20,27 @@
namespace android {
-static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject callback) {
- jclass callbackClass = env->GetObjectClass(callback);
- jmethodID callbackMethod = env->GetMethodID(callbackClass, "accept", "(IIJ)V");
- if (callbackMethod == 0) {
- return JNI_FALSE;
- }
-
- auto freqs = android::bpf::getCpuFreqs();
- if (!freqs) return JNI_FALSE;
+static jlongArray KernelCpuTotalBpfMapReader_readInternal(JNIEnv *env, jobject) {
auto freqTimes = android::bpf::getTotalCpuFreqTimes();
if (!freqTimes) return JNI_FALSE;
- auto freqsClusterSize = (*freqs).size();
- for (uint32_t clusterIndex = 0; clusterIndex < freqsClusterSize; ++clusterIndex) {
- auto freqsSize = (*freqs)[clusterIndex].size();
- for (uint32_t freqIndex = 0; freqIndex < freqsSize; ++freqIndex) {
- env->CallVoidMethod(callback, callbackMethod, clusterIndex,
- (*freqs)[clusterIndex][freqIndex],
- (*freqTimes)[clusterIndex][freqIndex] / 1000000);
+ std::vector<uint64_t> allTimes;
+ for (const auto &vec : *freqTimes) {
+ for (const auto &timeNs : vec) {
+ allTimes.push_back(timeNs / 1000000);
}
}
- return JNI_TRUE;
+
+ auto ar = env->NewLongArray(allTimes.size());
+ if (ar != NULL) {
+ env->SetLongArrayRegion(ar, 0, allTimes.size(),
+ reinterpret_cast<const jlong *>(allTimes.data()));
+ }
+ return ar;
}
static const JNINativeMethod methods[] = {
- {"read", "(Lcom/android/internal/os/KernelCpuTotalBpfMapReader$Callback;)Z",
- (void *)KernelCpuTotalBpfMapReader_read},
+ {"readInternal", "()[J", (void *)KernelCpuTotalBpfMapReader_readInternal},
};
int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv *env) {
diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
index 7c68de5..7900d30 100644
--- a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
@@ -82,25 +82,9 @@
return true;
}
-static jlongArray KernelCpuUidFreqTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
- auto freqs = android::bpf::getCpuFreqs();
- if (!freqs) return NULL;
-
- std::vector<uint64_t> allFreqs;
- for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));
-
- auto ar = env->NewLongArray(allFreqs.size());
- if (ar != NULL) {
- env->SetLongArrayRegion(ar, 0, allFreqs.size(),
- reinterpret_cast<const jlong *>(allFreqs.data()));
- }
- return ar;
-}
-
static const JNINativeMethod gFreqTimeMethods[] = {
{"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange},
{"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData},
- {"getDataDimensions", "()[J", (void *)KernelCpuUidFreqTimeBpfMapReader_getDataDimensions},
};
static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
@@ -186,10 +170,6 @@
{"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)},
};
-static jboolean KernelCpuUidBpfMapReader_startTrackingBpfTimes(JNIEnv *, jobject) {
- return android::bpf::startTrackingUidTimes();
-}
-
int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) {
gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray");
gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz);
@@ -198,14 +178,10 @@
gSparseArrayClassInfo.get =
GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;");
constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader";
- constexpr JNINativeMethod method = {"startTrackingBpfTimes", "()Z",
- (void *)KernelCpuUidBpfMapReader_startTrackingBpfTimes};
-
- int ret = RegisterMethodsOrDie(env, readerName, &method, 1);
- if (ret < 0) return ret;
auto c = FindClassOrDie(env, readerName);
gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;");
+ int ret = 0;
for (const auto &m : gAllMethods) {
auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name);
ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods);
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c882431..a1be865c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -406,6 +406,8 @@
optional int64 finished_seamless_rotation_frame = 40;
optional WindowFramesProto window_frames = 41;
optional bool force_seamless_rotation = 42;
+ optional bool in_size_compat_mode = 43;
+ optional float global_scale = 44;
}
message IdentifierProto {
@@ -516,6 +518,7 @@
optional .android.graphics.RectProto visible_insets = 13 [deprecated=true];
optional .android.graphics.RectProto stable_insets = 14 [deprecated=true];
optional .android.graphics.RectProto outsets = 15;
+ optional .android.graphics.RectProto compat_frame = 16;
}
message InsetsSourceProviderProto {
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 4cf9cfb..b988b5a 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -44,11 +44,73 @@
],
}
+genrule {
+ name: "remote-color-resources-compile-public",
+ tools: ["aapt2"],
+ srcs: [
+ "remote_color_resources_res/values/public.xml",
+ ],
+ out: ["values_public.arsc.flat"],
+ cmd: "$(location aapt2) compile $(in) -o $(genDir)",
+}
+
+genrule {
+ name: "remote-color-resources-compile-colors",
+ tools: ["aapt2"],
+ srcs: [
+ "remote_color_resources_res/values/colors.xml",
+ ],
+ out: ["values_colors.arsc.flat"],
+ cmd: "$(location aapt2) compile $(in) -o $(genDir)",
+}
+
+genrule {
+ name: "remote-color-resources-apk",
+ tools: ["aapt2"],
+ // The first input file in the list must be the manifest
+ srcs: [
+ "RemoteThemeColorsAndroidManifest.xml",
+ ":remote-color-resources-compile-public",
+ ":remote-color-resources-compile-colors",
+ ],
+ out: ["remote-color-resources.apk"],
+ cmd: "$(location aapt2) link -o $(out) --manifest $(in)"
+}
+
+genrule {
+ name: "remote-color-resources-arsc",
+ srcs: [":remote-color-resources-apk"],
+ out: ["res/raw/remote_views_color_resources.arsc"],
+ cmd: "mkdir -p $(genDir)/remote-color-resources-arsc && "
+ + "unzip -x $(in) resources.arsc -d $(genDir)/remote-color-resources-arsc && "
+ + "mkdir -p $$(dirname $(out)) && "
+ + "mv $(genDir)/remote-color-resources-arsc/resources.arsc $(out) && "
+ + "echo 'Created $(out)'"
+}
+
+genrule {
+ name: "remote-color-resources-arsc-zip",
+ tools: ["soong_zip"],
+ srcs: [
+ ":remote-color-resources-arsc",
+ "remote_color_resources_res/symbols.xml",
+ ],
+ out: ["remote_views_color_resources.zip"],
+ cmd: "INPUTS=($(in)) && "
+ + "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && "
+ + "mkdir -p $$RES_DIR/values && "
+ + "cp $${INPUTS[1]} $$RES_DIR/values && "
+ + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR && "
+ + "cp $(out) ."
+}
+
android_app {
name: "framework-res",
sdk_version: "core_platform",
certificate: "platform",
+ srcs: [":remote-color-resources-arsc"],
+
// Disable dexpreopt and verify_uses_libraries check as the app
// contains no Java code to be dexpreopted.
enforce_uses_libs: false,
@@ -72,6 +134,10 @@
"--auto-add-overlay",
],
+ resource_zips: [
+ ":remote-color-resources-arsc-zip",
+ ],
+
// Create package-export.apk, which other packages can use to get
// PRODUCT-agnostic resource data like IDs and type definitions.
export_package_resources: true,
@@ -96,6 +162,7 @@
srcs: [
"assets/**/*",
"res/**/*",
+ ":remote-color-resources-arsc",
],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a85996a..5dd8580 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3983,6 +3983,13 @@
<permission android:name="com.android.permission.USE_INSTALLER_V2"
android:protectionLevel="signature|verifier" />
+ <!-- Allows an application to use System Data Loaders.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi @TestApi Allows an application to clear user data.
<p>Not for use by third-party applications
@hide
diff --git a/core/res/RemoteThemeColorsAndroidManifest.xml b/core/res/RemoteThemeColorsAndroidManifest.xml
new file mode 100644
index 0000000..11970fd
--- /dev/null
+++ b/core/res/RemoteThemeColorsAndroidManifest.xml
@@ -0,0 +1,5 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+<application/>
+</manifest>
+
diff --git a/core/res/remote_color_resources_res/symbols.xml b/core/res/remote_color_resources_res/symbols.xml
new file mode 100644
index 0000000..82d5e3e
--- /dev/null
+++ b/core/res/remote_color_resources_res/symbols.xml
@@ -0,0 +1,4 @@
+<resources>
+ <!-- ARSC file used to overlay local colors when rendering a RemoteViews -->
+ <java-symbol type="raw" name="remote_views_color_resources" />
+</resources>
diff --git a/core/res/remote_color_resources_res/values/colors.xml b/core/res/remote_color_resources_res/values/colors.xml
new file mode 100644
index 0000000..295f16e
--- /dev/null
+++ b/core/res/remote_color_resources_res/values/colors.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Note: the values of the colors doesn't really matter (they will always be overwritten before used), but they help a lot debugging, to find out which color is where in the ARSC file. -->
+ <color name="system_primary_0">#01010101</color>
+ <color name="system_primary_50">#02020202</color>
+ <color name="system_primary_100">#03030303</color>
+ <color name="system_primary_200">#04040404</color>
+ <color name="system_primary_300">#05050505</color>
+ <color name="system_primary_400">#06060606</color>
+ <color name="system_primary_500">#07070707</color>
+ <color name="system_primary_600">#08080808</color>
+ <color name="system_primary_700">#09090909</color>
+ <color name="system_primary_800">#0a0a0a0a</color>
+ <color name="system_primary_900">#0b0b0b0b</color>
+ <color name="system_primary_1000">#0c0c0c0c</color>
+ <color name="system_secondary_0">#10101010</color>
+ <color name="system_secondary_50">#20202020</color>
+ <color name="system_secondary_100">#30303030</color>
+ <color name="system_secondary_200">#40404040</color>
+ <color name="system_secondary_300">#50505050</color>
+ <color name="system_secondary_400">#60606060</color>
+ <color name="system_secondary_500">#70707070</color>
+ <color name="system_secondary_600">#80808080</color>
+ <color name="system_secondary_700">#90909090</color>
+ <color name="system_secondary_800">#a0a0a0a0</color>
+ <color name="system_secondary_900">#b0b0b0b0</color>
+ <color name="system_secondary_1000">#c0c0c0c0</color>
+ <color name="system_neutral_0">#1f1f1f1f</color>
+ <color name="system_neutral_50">#2f2f2f2f</color>
+ <color name="system_neutral_100">#3f3f3f3f</color>
+ <color name="system_neutral_200">#4f4f4f4f</color>
+ <color name="system_neutral_300">#5f5f5f5f</color>
+ <color name="system_neutral_400">#6f6f6f6f</color>
+ <color name="system_neutral_500">#7f7f7f7f</color>
+ <color name="system_neutral_600">#8f8f8f8f</color>
+ <color name="system_neutral_700">#9f9f9f9f</color>
+ <color name="system_neutral_800">#afafafaf</color>
+ <color name="system_neutral_900">#bfbfbfbf</color>
+ <color name="system_neutral_1000">#cfcfcfcf</color>
+</resources>
diff --git a/core/res/remote_color_resources_res/values/public.xml b/core/res/remote_color_resources_res/values/public.xml
new file mode 100644
index 0000000..e628f09
--- /dev/null
+++ b/core/res/remote_color_resources_res/values/public.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <public-group type="color" first-id="0x0106001d">
+ <public name="system_primary_0" />
+ <public name="system_primary_50" />
+ <public name="system_primary_100" />
+ <public name="system_primary_200" />
+ <public name="system_primary_300" />
+ <public name="system_primary_400" />
+ <public name="system_primary_500" />
+ <public name="system_primary_600" />
+ <public name="system_primary_700" />
+ <public name="system_primary_800" />
+ <public name="system_primary_900" />
+ <public name="system_primary_1000" />
+ <public name="system_secondary_0" />
+ <public name="system_secondary_50" />
+ <public name="system_secondary_100" />
+ <public name="system_secondary_200" />
+ <public name="system_secondary_300" />
+ <public name="system_secondary_400" />
+ <public name="system_secondary_500" />
+ <public name="system_secondary_600" />
+ <public name="system_secondary_700" />
+ <public name="system_secondary_800" />
+ <public name="system_secondary_900" />
+ <public name="system_secondary_1000" />
+ <public name="system_neutral_0" />
+ <public name="system_neutral_50" />
+ <public name="system_neutral_100" />
+ <public name="system_neutral_200" />
+ <public name="system_neutral_300" />
+ <public name="system_neutral_400" />
+ <public name="system_neutral_500" />
+ <public name="system_neutral_600" />
+ <public name="system_neutral_700" />
+ <public name="system_neutral_800" />
+ <public name="system_neutral_900" />
+ <public name="system_neutral_1000" />
+ </public-group>
+</resources>
diff --git a/core/res/res/drawable/toast_frame.xml b/core/res/res/drawable/toast_frame.xml
index d57bd6a..44c00c0 100644
--- a/core/res/res/drawable/toast_frame.xml
+++ b/core/res/res/drawable/toast_frame.xml
@@ -17,8 +17,7 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <!-- background is material_grey_200 with .9 alpha -->
- <solid android:color="#E6EEEEEE" />
- <corners android:radius="22dp" />
+ <solid android:color="?android:attr/colorBackground" />
+ <corners android:radius="28dp" />
</shape>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 6211463..1de1d04 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -17,8 +17,8 @@
<NotificationHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_header"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_header_solo_height"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_header_height"
android:layout_marginBottom="@dimen/notification_header_margin_bottom"
android:clipChildren="false"
android:gravity="center_vertical"
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index de537b2..2d1c342 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -38,11 +38,7 @@
android:layout_gravity="top"
>
- <include
- layout="@layout/notification_template_header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_header_big_height"
- />
+ <include layout="@layout/notification_template_header" />
<LinearLayout
android:id="@+id/notification_main_column"
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index 696cb65..bdd4430 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -36,7 +36,6 @@
layout="@layout/notification_template_header"
android:layout_width="match_parent"
android:layout_height="@dimen/media_notification_header_height"
- android:layout_gravity="start"
/>
<LinearLayout
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index e1b7bc4..6f3c77f 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -23,11 +23,7 @@
android:clipChildren="false"
>
- <include
- layout="@layout/notification_template_header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_header_big_height"
- />
+ <include layout="@layout/notification_template_header" />
<include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 2452a32..2954ba2 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -23,11 +23,7 @@
android:tag="bigText"
>
- <include
- layout="@layout/notification_template_header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_header_big_height"
- />
+ <include layout="@layout/notification_template_header" />
<com.android.internal.widget.RemeasuringLinearLayout
android:id="@+id/notification_action_list_margin_target"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 7daccd2..baffdd5 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -32,7 +32,8 @@
/>
<include layout="@layout/notification_template_header"
android:layout_width="match_parent"
- android:layout_height="@dimen/media_notification_header_height" />
+ android:layout_height="@dimen/media_notification_header_height"
+ />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout/transient_notification.xml b/core/res/res/layout/transient_notification.xml
index db586ec..8fcb77f 100644
--- a/core/res/res/layout/transient_notification.xml
+++ b/core/res/res/layout/transient_notification.xml
@@ -18,24 +18,27 @@
*/
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="?android:attr/toastFrameBackground">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:maxWidth="@dimen/toast_width"
+ android:background="?android:attr/toastFrameBackground"
+ android:layout_marginEnd="16dp"
+ android:layout_marginStart="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp">
<TextView
android:id="@android:id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginHorizontal="24dp"
- android:layout_marginVertical="15dp"
- android:layout_gravity="center_horizontal"
- android:textAppearance="@style/TextAppearance.Toast"
- android:textColor="@color/primary_text_default_material_light"
- />
-
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:lineHeight="20sp"
+ android:textAppearance="@style/TextAppearance.Toast"/>
</LinearLayout>
-
-
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index d7474d0..aa4baf3 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die program toe om Moenie Steur Nie-opstelling te lees en skryf."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"begin kyk van toestemminggebruik"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n program te begin. Behoort nooit vir normale programme nodig te wees nie."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"kry toegang tot sensordata teen \'n hoë monsternemingkoers"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Laat die program toe om monsters van sensordata teen \'n hoër koers as 200 Hz te neem"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Stel wagwoordreëls"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Beheer die lengte en die karakters wat in skermslotwagwoorde en -PIN\'e toegelaat word."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor pogings om skerm te ontsluit"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-versoek is na video-oproep toe verander"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-versoek is na USSD-versoek toe verander"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Na nuwe SS-versoek toe verander"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Werkprofiel"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Kennisgewing gegee"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Vou uit"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 8283584..9b11350 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"መተግበሪያው የአትረብሽ ውቅረትን እንዲያነብብ እና እንዲጸፍ ይፈቅዳል።"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"የእይታ ፈቃድ መጠቀምን መጀመር"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ያዢው ለአንድ መተግበሪያ የፈቃድ አጠቃቀሙን እንዲያስጀምር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ ሊያስፈልግ አይገባም።"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"የዳሳሽ ውሂቡን በከፍተኛ የናሙና ብዛት ላይ ይድረሱበት"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"መተግበሪያው የዳሳሽ ውሂቡን ከ200 ኸ በሚበልጥ ፍጥነት ናሙና እንዲያደርግ ይፈቅድለታል"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"የይለፍ ቃል ደንቦች አዘጋጅ"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"በማያ ገጽ መቆለፊያ የይለፍ ቃሎች እና ፒኖች ውስጥ የሚፈቀዱ ቁምፊዎችን እና ርዝመታቸውን ተቆጣጠር።"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"የማሳያ-ክፈት ሙከራዎችን ክትትል ያድርጉባቸው"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"የSS ጥያቄ ወደ የቪዲዮ ጥሪ ተለውጧል"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"የSS ጥያቄ ወደ የUSSD ጥያቄ ተለውጧል"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ወደ አዲስ የSS ጥያቄ ተለውጧል"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"የስራ መገለጫ"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"ነቅተዋል"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ዘርጋ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index d0dd5fb..bbf30fe 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -697,10 +697,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة إعداد \"عدم الإزعاج\" وكتابتها."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"بدء استخدام إذن العرض"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"للسماح للمالك ببدء استخدام الإذن لأحد التطبيقات. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"الوصول إلى بيانات جهاز الاستشعار بمعدّل مرتفع للبيانات في الملف الصوتي"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"يسمح هذا الأذن للتطبيق بزيادة بيانات جهاز الاستشعار بمعدّل بيانات في الملف الصوتي أكبر من 200 هرتز."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"تعيين قواعد كلمة المرور"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"للتحكم في الطول والأحرف المسموح بها في كلمات المرور وأرقام التعريف الشخصي في قفل الشاشة."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"مراقبة محاولات فتح قفل الشاشة"</string>
@@ -1997,6 +1995,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"تم تغيير طلب SS إلى مكالمة فيديو."</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"تم تغيير طلب SS إلى طلب USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"تم التغيير إلى طلب SS الجديد."</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"الملف الشخصي للعمل"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"تمّ تفعيل التنبيه"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"توسيع"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f6fc09f..e4f657f 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অসুবিধা নিদিবৰ কনফিগাৰেশ্বনক পঢ়িবলৈ আৰু সালসলনি কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"চোৱাৰ অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰক"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ধাৰকক কোনো এপৰ বাবে অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰিবলৈ দিয়ে। সাধাৰণ এপ্সমূহৰ বাবে কেতিয়াও প্ৰয়োজন হ’ব নালাগে।"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"এটা উচ্চ ছেম্পলিঙৰ হাৰত ছেন্সৰৰ ডেটা এক্সেছ কৰে"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"এপ্টোক ২০০ হাৰ্টজতকৈ অধিক হাৰত ছেন্সৰৰ ডেটাৰ নমুনা ল’বলৈ অনুমতি দিয়ে"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"পাছৱর্ডৰ নিয়ম ছেট কৰক"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"স্ক্ৰীণ লক পাছৱৰ্ড আৰু পিনৰ দৈর্ঘ্য আৰু কি কি আখৰ ব্যৱহাৰ কৰিব পাৰে তাক নিয়ন্ত্ৰণ কৰক।"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্ৰীণ আনলক কৰা প্ৰয়াসবোৰ পৰ্যবেক্ষণ কৰিব পাৰে"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ’ল"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS অনুৰোধ USSD অনুৰোধলৈ সলনি কৰা হ’ল"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"নতুন SS অনুৰোধলৈ সলনি কৰা হ’ল"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ফিশ্বিঙৰ সতৰ্কবাৰ্তা"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"সতৰ্ক কৰা হ’ল"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"বিস্তাৰ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 58d225d..549784d 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Tətbiqə \"Narahat Etməyin\" konfiqurasiyasını oxumağa və yazmağa icazə verin."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Baxış icazəsinin istifadəsinə başlayın"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Sahibinə tətbiqin icazədən istifadəsinə başlamağa imkan verir. Adi tətbiqlər üçün heç vaxt tələb edilmir."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"sensor datasına yüksək ölçmə sürəti ilə giriş etmək"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Tətbiqə sensor datasını 200 Hz-dən yüksək sürətlə ölçməyə imkan verir"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Parol qaydalarını təyin edin"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Ekran kilidinin parolu və PINlərində icazə verilən uzunluq və simvollara nəzarət edin."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranı kiliddən çıxarmaq üçün edilən cəhdlərə nəzarət edin"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS sorğusu video zəngə dəyişdirildi"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS sorğusu USSD sorğusuna dəyişdirildi"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Yeni SS sorğusuna dəyişdirildi"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"İş profili"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Xəbərdarlıq edildi"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Genişləndirin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 1569bd1..5709e3d 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -688,10 +688,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Dozvoljava aplikaciji da čita i upisuje konfiguraciju podešavanja Ne uznemiravaj."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"početak korišćenja dozvole za pregled"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da započne korišćenje dozvole za aplikaciju. Nikada ne bi trebalo da bude potrebna za uobičajene aplikacije."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora pri velikoj brzini uzorkovanja"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Dozvoljava aplikaciji da uzima uzorak podataka senzora pri brzini većoj od 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Podešavanje pravila za lozinku"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontroliše dužinu i znakove dozvoljene u lozinkama i PIN-ovima za zaključavanje ekrana."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadgledajte pokušaje otključavanja ekrana"</string>
@@ -1904,6 +1902,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS zahtev je promenjen u video poziv"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS zahtev je promenjen u USSD zahtev"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Promenjeno je u novi SS zahtev"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Upozorenje o „pecanju“"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Poslovni profil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Obavešteno"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Proširi"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index f1f7e0c..880c986 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -691,10 +691,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дазваляе праграме чытаць і выконваць запіс у канфігурацыю рэжыму «Не турбаваць»."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"запусціць выкарыстанне дазволаў на прагляд"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дазваляе трымальніку запусціць выкарыстанне дазволаў праграмай. Не патрэбна для звычайных праграм."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"атрымліваць даныя датчыка з высокай частатой дыскрэтызацыі"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Праграма зможа распазнаваць даныя датчыка з частатой звыш 200 Гц"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Устанавіць правілы паролю"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Кіраваць даўжынёй і сімваламі, дазволенымі пры ўводзе пароляў і PIN-кодаў блакіроўкі экрана."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Сачыць за спробамі разблакіроўкі экрана"</string>
@@ -1935,6 +1933,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-запыт заменены на відэавыклік"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-запыт заменены на USSD-запыт"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Зроблена замена на новы SS-запыт"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Працоўны профіль"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"З гукам"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Разгарнуць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 076df22..2ed363f 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Предоставя на приложението достъп за четене и запис до конфигурацията на „Не безпокойте“."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"стартиране на прегледа на използваните разрешения"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Разрешава на притежателя да стартира прегледа на използваните разрешения за дадено приложение. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"осъществяване на достъп до данните от сензорите при висока скорост на семплиране"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Разрешава на приложението да семплира данните от сензорите със скорост, по-висока от 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Задаване на правила за паролата"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролира дължината и разрешените знаци за паролите и ПИН кодовете за заключване на екрана."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Наблюдаване на опитите за отключване на екрана"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS заявката е променена на видеообаждане"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS заявката е променена на USSD заявка"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Променено на нова SS заявка"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Сигнал за фишинг"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Служебен потребителски профил"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Сигналът е изпратен"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Разгъване"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index c7fb8bd..dd38e9a 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অ্যাপটিকে \'বিরক্ত করবে না\' কনফিগারেশন পড়া এবং লেখার অনুমতি দেয়।"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"দেখার অনুমতি কাজে লাগানো শুরু করুন"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"কোনও অ্যাপের কোনও নির্দিষ্ট অনুমতির ব্যবহার শুরু করার ক্ষেত্রে হোল্ডারকে সাহায্য করে। সাধারণ অ্যাপের জন্য এটির পরিবর্তন হওয়ার কথা নয়।"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"হাই স্যাম্পলিং রেটে সেন্সর ডেটা অ্যাক্সেস করুন"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz-এর বেশি রেটে অ্যাপকে স্যাম্পল সেন্সর ডেটার জন্য অনুমতি দিন"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"পাসওয়ার্ড নিয়মগুলি সেট করে"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"স্ক্রিন লক করার পাসওয়ার্ডগুলিতে অনুমতিপ্রাপ্ত অক্ষর এবং দৈর্ঘ্য নিয়ন্ত্রণ করে৷"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্রিন আনলক করার প্রচেষ্টাগুলির উপরে নজর রাখুন"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS অনুরোধ ভিডিও কলে পরিবর্তন করা হয়েছে"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS অনুরোধ USSD অনুরোধে পরিবর্তন করা হয়েছে"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"নতুন SS অনুরোধে পরিবর্তন করা হয়েছে"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"কর্মস্থলের প্রোফাইল"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"সতর্ক করা হয়েছে"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"বড় করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 393e702..d5b10d5 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -688,10 +688,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućava aplikaciji da čita i upisuje konfiguraciju načina rada Ne ometaj."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti korištenje odobrenja za pregled"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da pokrene korištenje odobrenja za aplikaciju. Ne bi trebalo biti potrebno za obične aplikacije."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora velikom brzinom uzorkovanja"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Dozvoljava aplikaciji da uzorkuje podatke senzora većom brzinom od 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Postavljanje pravila za lozinke"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolira dužinu i znakove koji su dozvoljeni u lozinkama za zaključavanje ekrana i PIN-ovima."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Prati pokušaje otključavanja ekrana"</string>
@@ -1904,6 +1902,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS zahtjev je promijenjen u video poziv"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS zahtjev je promijenjen u USSD zahtjev"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Promijenjeno u novi SS zahtjev"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Upozorenje o krađi identiteta"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil za posao"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Upozoreni"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Proširi"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 56131e3..280fe1a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet que l\'aplicació llegeixi la configuració No molestis i hi escrigui."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"comença a utilitzar el permís de visualització"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet que un propietari comenci a utilitzar el permís amb una aplicació. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accedir a les dades del sensor a una freqüència de mostratge alta"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permet que l\'aplicació dugui a terme un mostratge de les dades del sensor a una freqüència superior a 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Definir les normes de contrasenya"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Permet controlar la longitud i el nombre de caràcters permesos a les contrasenyes i als PIN del bloqueig de pantalla."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar els intents de desbloqueig de la pantalla"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"La sol·licitud SS s\'ha canviat per una videotrucada"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"La sol·licitud SS s\'ha canviat per una sol·licitud USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"S\'ha canviat a una nova sol·licitud SS"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de pesca de credencials"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de treball"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"S\'ha enviat una alerta"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Desplega"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 7f20d3f..091ed83 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -691,10 +691,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikaci číst a zapisovat konfiguraci režimu Nerušit."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"zahájení zobrazení využití oprávnění"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje přístup zahájit využití oprávnění jiné aplikace. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"přístup k datům ze senzorů s vyšší vzorkovací frekvencí"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Umožňuje aplikaci vzorkovat data ze senzorů s frekvencí vyšší než 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Nastavit pravidla pro heslo"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Ovládání délky a znaků povolených v heslech a kódech PIN zámku obrazovky."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovat pokusy o odemknutí obrazovky"</string>
@@ -1935,6 +1933,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Požadavek SS byl změněn na videohovor"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Požadavek SS byl změněn na požadavek USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Změněno na nový požadavek SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Pracovní profil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Upozorněno"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Rozbalit"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index d0effa6..8e19100 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -687,10 +687,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Giver appen tilladelse til at læse og redigere konfigurationen af Forstyr ikke."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start brugen at tilladelsesvisning"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Tillader, at brugeren kan bruge en tilladelse for en app. Dette bør aldrig være nødvendigt for almindelige apps."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"få adgang til sensordata ved høj samplingfrekvens"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Tillader, at appen kan sample sensordata ved en højere frekvens end 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Angiv regler for adgangskoder"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Tjek længden samt tilladte tegn i adgangskoder og pinkoder til skærmlåsen."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåg forsøg på oplåsning af skærm"</string>
@@ -1875,6 +1873,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-anmodningen blev ændret til et videoopkald"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-anmodningen blev ændret til en USSD-anmodning"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Ændret til en SS-anmodning"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Arbejdsprofil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Underrettet"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Udvid"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 7045941..d9710e6 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ermöglicht der App Lese- und Schreibzugriff auf die \"Bitte nicht stören\"-Konfiguration"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Mit der Verwendung der Anzeigeberechtigung beginnen"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ermöglicht dem Inhaber, die Berechtigungsnutzung für eine App zu beginnen. Sollte für normale Apps nie benötigt werden."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Sensordaten mit hoher Frequenz auslesen"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Erlaubt der App, Sensordaten mit einer Frequenz von mehr als 200 Hz auszulesen"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Passwortregeln festlegen"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Zulässige Länge und Zeichen für Passwörter für die Displaysperre festlegen"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Versuche zum Entsperren des Displays überwachen"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-Anfrage wurde in Videoanruf geändert"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-Anfrage wurde in USSD-Anfrage geändert"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"In neue SS-Anfrage geändert"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing-Warnung"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Arbeitsprofil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Gewarnt"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Maximieren"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8d14b3b..358a1f1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Επιτρέπει στην εφαρμογή την εγγραφή και τη σύνταξη διαμόρφωσης για τη λειτουργία \"Μην ενοχλείτε\"."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"έναρξη χρήσης άδειας προβολής"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Επιτρέπει στον κάτοχο να ξεκινήσει τη χρήση της άδειας για μια εφαρμογή. Δεν απαιτείται ποτέ για κανονικές εφαρμογές."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"πρόσβαση σε δεδομένα αισθητήρα με υψηλό ρυθμό δειγματοληψίας"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Επιτρέπει στην εφαρμογή τη δειγματοληψία των δεδομένων αισθητήρα με ρυθμό μεγαλύτερο από 200 Hz."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Ορισμός κανόνων κωδικού πρόσβασης"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Ελέγξτε την έκταση και τους επιτρεπόμενους χαρακτήρες σε κωδικούς πρόσβασης κλειδώματος οθόνης και PIN."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Παρακολούθηση προσπαθειών ξεκλειδώματος οθόνης"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Το αίτημα SS τροποποιήθηκε σε βιντεοκλήση"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Το αίτημα SS τροποποιήθηκε σε αίτημα USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Τροποποιήθηκε σε νέο αίτημα SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Προφίλ εργασίας"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Ειδοποιήθηκε"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Ανάπτυξη"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 51e4ef7..058ae77 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 3e99738..d91b3d0 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 464a44b..60950ee 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 2b8cd86..1915d45 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 9d073b9..0a90aeac 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 333bdc8..e81d6d1 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1871,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Se cambió la solicitud SS por una videollamada"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Se cambió la solicitud SS por una solicitud USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Se cambió a una nueva solicitud SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabajo"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerta enviada"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expandir"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 476193e..70cac4a 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de No molestar."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de visualización"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el titular inicie el uso de permisos de una aplicación. Las aplicaciones normales no deberían necesitar nunca este permiso."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acceder a datos de sensores a una frecuencia de muestreo alta"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que la aplicación consulte datos de sensores a una frecuencia superior a 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Establecimiento de reglas de contraseña"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla la longitud y los caracteres permitidos en los PIN y en las contraseñas de bloqueo de pantalla."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar los intentos de desbloqueo de pantalla"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Se ha cambiado la solicitud de SS a una videollamada"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Se ha cambiado la solicitud de SS a una solicitud de USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Se ha cambiado a una nueva solicitud de SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabajo"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Con sonido"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Mostrar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index c32cf73..2a2db3a 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Võimaldab rakendusel lugeda ja kirjutada funktsiooni Mitte segada seadistusi."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"vaatamisloa kasutamise alustamine"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Võimaldab omanikul rakenduse puhul alustada loa kasutamist. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"juurdepääs anduri andmetele kõrgel diskreetimissagedusel"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Võimaldab rakendusel anduri andmeid diskreetida sagedusel, mis on suurem kui 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Parooli reeglite määramine"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Juhitakse ekraaniluku paroolide ja PIN-koodide pikkusi ning lubatud tähemärkide seadeid."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekraani avamiskatsete jälgimine"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-taotlus muudeti videokõneks"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-taotlus muudeti USSD-taotluseks"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Muudeti uueks SS-taotluseks"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Tööprofiil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Teavitatud"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Laienda"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 86546e0b..901d8661 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -251,7 +251,7 @@
<string name="global_action_logout" msgid="6093581310002476511">"Amaitu saioa"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"Pantaila-argazkia"</string>
<string name="bugreport_title" msgid="8549990811777373050">"Akatsen txostena"</string>
- <string name="bugreport_message" msgid="5212529146119624326">"Gailuaren uneko egoerari buruzko informazioa bilduko da, mezu elektroniko gisa bidaltzeko. Minutu batzuk igaroko dira akatsen txostena sortzen hasten denetik bidaltzeko prest egon arte. Itxaron, mesedez."</string>
+ <string name="bugreport_message" msgid="5212529146119624326">"Gailuaren oraingo egoerari buruzko informazioa bilduko da, mezu elektroniko gisa bidaltzeko. Minutu batzuk igaroko dira akatsen txostena sortzen hasten denetik bidaltzeko prest egon arte. Itxaron, mesedez."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Txosten dinamikoa"</string>
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Aukera hau erabili beharko zenuke ia beti. Txostenaren jarraipena egin ahal izango duzu eta arazoari buruzko xehetasunak eman ahal izango dituzu. Baliteke gutxitan erabili behar izaten diren atalak ez agertzea, denbora aurrezteko."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Txosten osoa"</string>
@@ -674,7 +674,7 @@
<string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"atzitu DRM ziurtagiriak"</string>
<string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM ziurtagiriak hornitzea eta erabiltzeko baimena ematen die aplikazioei. Aplikazio normalek ez lukete beharko."</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"Jaso Android Beam transferentzien egoera"</string>
- <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Uneko Android Beam transferentziei buruzko informazioa jasotzeko baimena ematen die aplikazioei"</string>
+ <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Oraingo Android Beam transferentziei buruzko informazioa jasotzeko baimena ematen die aplikazioei"</string>
<string name="permlab_removeDrmCertificates" msgid="710576248717404416">"kendu DRM ziurtagiriak"</string>
<string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM ziurtagiriak kentzea baimentzen die aplikazioei. Aplikazio normalek ez lukete beharko."</string>
<string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"lotu operadorearen mezularitza-zerbitzuari"</string>
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ez molestatzeko moduaren konfigurazioa irakurtzeko eta bertan idazteko baimena ematen die aplikazioei."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"hasi ikusteko baimena erabiltzen"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Aplikazioaren baimena erabiltzen hasteko baimena ematen die titularrei. Aplikazio normalek ez lukete beharko."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"atzitu sentsoreen datuen laginak abiadura handian"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Aplikazioak 200 Hz-tik gorako abiaduran hartu ahal izango ditu sentsoreen datuen laginak"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Ezarri pasahitzen arauak"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolatu pantaila blokeoaren pasahitzen eta PINen luzera eta onartutako karaktereak."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gainbegiratu pantaila desblokeatzeko saiakerak"</string>
@@ -1201,7 +1199,7 @@
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"Eskala"</string>
<string name="screen_compat_mode_show" msgid="5080361367584709857">"Erakutsi beti"</string>
<string name="screen_compat_mode_hint" msgid="4032272159093750908">"Gaitu hori berriro Sistemaren ezarpenak > Aplikazioak > Deskargatutakoak."</string>
- <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez du onartzen uneko pantailaren tamaina eta espero ez bezala joka lezake."</string>
+ <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez du onartzen pantailaren tamaina, eta baliteke espero ez bezala jokatzea."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"Erakutsi beti"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"Android sistema eragilearen bertsio bateraezin baterako dago egina <xliff:g id="APP_NAME">%1$s</xliff:g>; beraz, espero ez bezala funtziona lezake. Baliteke aplikazioaren bertsio eguneratuago bat eskuragarri egotea."</string>
<string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Erakutsi beti"</string>
@@ -1231,9 +1229,9 @@
<string name="new_app_description" msgid="1958903080400806644">"Gorde gabe itxiko da <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
<string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak memoria-muga gainditu du"</string>
<string name="dump_heap_ready_notification" msgid="2302452262927390268">"Prest dago <xliff:g id="PROC">%1$s</xliff:g> memoria-iraulketaren txostena"</string>
- <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Sortu da uneko memoria-iraulketaren txostena. Sakatu partekatzeko."</string>
- <string name="dump_heap_title" msgid="4367128917229233901">"Uneko memoria-iraulketaren txostena partekatu nahi duzu?"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak memoria-muga (<xliff:g id="SIZE">%2$s</xliff:g>) gainditu du. Uneko memoria-iraulketaren txostena sortu da, garatzailearekin parteka dezazun. Kontuz: baliteke txosten horrek aplikazioak atzi dezakeen informazio pertsonala izatea."</string>
+ <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Sortu da memoria-iraulketaren txostena. Sakatu partekatzeko."</string>
+ <string name="dump_heap_title" msgid="4367128917229233901">"Memoria-iraulketaren txostena partekatu nahi duzu?"</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak memoria-muga (<xliff:g id="SIZE">%2$s</xliff:g>) gainditu du. Memoria-iraulketaren txostena sortu da, garatzailearekin parteka dezazun. Kontuz: baliteke txosten horrek aplikazioak atzi dezakeen informazio pertsonala izatea."</string>
<string name="dump_heap_system_text" msgid="6805155514925350849">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak bere memoria-muga (<xliff:g id="SIZE">%2$s</xliff:g>) gainditu du. Memoria-iraulketaren txosten bat duzu erabilgarri, hura partekatu nahi baduzu ere. Kontuz: baliteke txosten horrek prozesuak atzi dezakeen kontuzko informazio pertsonala izatea eta datu horien barnean zuk idatzitakoak egotea, besteak beste."</string>
<string name="dump_heap_ready_text" msgid="5849618132123045516">"<xliff:g id="PROC">%1$s</xliff:g> prozesuaren memoria-iraulketaren txosten bat duzu erabilgarri, hura partekatu nahi baduzu ere. Kontuz: baliteke txosten horrek prozesuak atzi dezakeen kontuzko informazio pertsonala izatea eta datu horien barnean zuk idatzitakoak egotea, besteak beste."</string>
<string name="sendText" msgid="493003724401350724">"Aukeratu testurako ekintza"</string>
@@ -1361,7 +1359,7 @@
<string name="alert_windows_notification_message" msgid="6538171456970725333">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string>
<string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desaktibatu"</string>
<string name="ext_media_checking_notification_title" msgid="8299199995416510094">"<xliff:g id="NAME">%s</xliff:g> egiaztatzen…"</string>
- <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"Uneko edukia berrikusten"</string>
+ <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"Edukia berrikusten"</string>
<string name="ext_media_new_notification_title" msgid="3517407571407687677">"Euskarri berria: <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> ez da funtzionatzen ari"</string>
<string name="ext_media_new_notification_message" msgid="6095403121990786986">"Sakatu konfiguratzeko"</string>
@@ -1642,7 +1640,7 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erabilerraztasun-lasterbidea erabili nahi duzu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Lasterbidea aktibatuta dagoenean, bi bolumen-botoiak hiru segundoz sakatuta abiaraziko da erabilerraztasun-eginbidea."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Erabilerraztasun-eginbideetarako lasterbidea aktibatu nahi duzu?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Eduki sakatuta bolumen-botoiak segundo batzuez erabilerraztasun-eginbideak aktibatzeko. Hori eginez gero, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nUneko eginbideak:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nHautatutako eginbideak aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Eduki sakatuta bolumen-botoiak segundo batzuez erabilerraztasun-eginbideak aktibatzeko. Hori eginez gero, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nEginbideak:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nHautatutako eginbideak aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> zerbitzuaren lasterbidea aktibatu nahi duzu?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Eduki sakatuta bolumen-botoiak segundo batzuez <xliff:g id="SERVICE">%1$s</xliff:g> izeneko erabilerraztasun-eginbidea aktibatzeko. Honen bidez, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nLasterbide hau beste eginbide batengatik aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string>
@@ -1680,7 +1678,7 @@
<string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Eginbide batetik bestera aldatzeko, pasatu bi hatz pantailaren behealdetik gora eta eduki sakatuta une batez."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Eginbide batetik bestera aldatzeko, pasatu hiru hatz pantailaren behealdetik gora eta eduki sakatuta une batez."</string>
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Lupa"</string>
- <string name="user_switched" msgid="7249833311585228097">"Uneko erabiltzailea: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="user_switched" msgid="7249833311585228097">"Erabiltzailea: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzailera aldatzen…"</string>
<string name="user_logging_out_message" msgid="7216437629179710359">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzailearen saioa amaitzen…"</string>
<string name="owner_name" msgid="8713560351570795743">"Jabea"</string>
@@ -1780,7 +1778,7 @@
<string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Idatzi administratzailearen PIN kodea"</string>
<string name="restr_pin_enter_pin" msgid="373139384161304555">"Idatzi PINa"</string>
<string name="restr_pin_incorrect" msgid="3861383632940852496">"Okerra"</string>
- <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Uneko PINa"</string>
+ <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Oraingo PINa"</string>
<string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"PIN berria"</string>
<string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Berretsi PIN berria"</string>
<string name="restr_pin_create_pin" msgid="917067613896366033">"Konfiguratu debekuak aldatu ahal izateko idatzi beharko den PIN kodea"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS eskaera bideo-deira aldatu da"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS eskaera USSD eskaerara aldatu da"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"SS eskaera berrira aldatu da"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing-alerta"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profila"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Egin du soinua"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Zabaldu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b1ec1f6..ec195e5 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"به برنامه امکان میدهد پیکربندی «مزاحم نشوید» را بخواند و بنویسد."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"شروع مشاهده استفاده از مجوز"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"به دارنده اجازه شروع استفاده از مجوز را برای برنامه میدهد. هرگز برای برنامههای معمول نیاز نیست."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"دسترسی به دادههای حسگر با نرخ نمونهبرداری بالا"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"به برنامه اجازه میدهد دادههای حسگر را با نرخ بیشاز ۲۰۰ هرتز نمونهبرداری کند"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"تنظیم قوانین گذرواژه"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"کنترل طول و نوع نویسههایی که در گذرواژه و پین قفل صفحه مجاز است."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"پایش تلاشهای باز کردن قفل صفحه"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"درخواست SS به تماس تصویری تغییر کرد"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"درخواست SS به درخواست USSD تغییر کرد"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"به درخواست SS جدید تغییر کرد"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"نمایه کاری"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"هشدار ارسال شد"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"بزرگ کردن"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 55c6d92..6773dde 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Sallii sovelluksen lukea ja muokata Älä häiritse -tilan asetuksia."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"aloita katseluoikeuksien käyttö"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Antaa luvanhaltijan käynnistää sovelluksen käyttöoikeuksien käytön. Ei tavallisten sovelluksien käyttöön."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"saada pääsyn anturidataan suuremmalla näytteenottotaajuudella"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Sallii sovelluksen ottaa anturidatasta näytteitä yli 200 Hz:n taajuudella"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Asentaa salasanasäännöt"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Hallinnoida ruudun lukituksen salasanoissa ja PIN-koodeissa sallittuja merkkejä ja niiden pituutta."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Tarkkailla näytön avaamisyrityksiä"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-pyyntö vaihdettu videopuheluksi"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-pyyntö vaihdettu USSD-pyynnöksi"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Vaihdettu uudeksi SS-pyynnöksi"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Työprofiili"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Hälytti"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Laajenna"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 15351bc..9c4f92a 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1871,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"La demande SS a été remplacée par une demande d\'appel vidéo"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"La demande SS a été remplacée par une demande USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"La demande a été remplacée par une nouvelle demande SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil professionnel"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerté"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Développer"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 8cb8614..78af86f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet à l\'application de consulter et de modifier la configuration du mode Ne pas déranger."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"activer l\'utilisation de l\'autorisation d\'affichage"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet à l\'application autorisée d\'activer l\'utilisation de l\'autorisation pour une application. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accéder aux données des capteurs à un taux d\'échantillonnage élevé"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Autorise l\'appli à échantillonner les données des capteurs à un taux supérieur à 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Définir les règles du mot de passe"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Gérer le nombre et le type de caractères autorisés dans les mots de passe et les codes d\'accès de verrouillage de l\'écran"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gérer les tentatives de déverrouillage de l\'écran"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Requête SS transformée en appel vidéo"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Requête SS transformée en requête USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Remplacement par une nouvelle requête SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil professionnel"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerte envoyée"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Développer"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index f99d02c..949e486 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite á aplicación ler e escribir a configuración do modo Non molestar."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite ao propietario iniciar o uso de permisos dunha aplicación. As aplicacións normais non deberían precisalo nunca."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acceder aos datos dos sensores usando unha taxa de mostraxe alta"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que a aplicación recompile mostras dos datos dos sensores cunha taxa superior a 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Establecer as normas de contrasinal"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla a lonxitude e os caracteres permitidos nos contrasinais e nos PIN de bloqueo da pantalla."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Controlar os intentos de desbloqueo da pantalla"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"A solicitude SS transformouse nunha videochamada"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"A solicitude SS transformouse nunha solicitude USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Transformouse nunha nova solicitude SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de traballo"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Con son"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Despregar"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 84ee732..29ce76b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"એપ્લિકેશનને ખલેલ પાડશો નહીં ગોઠવણી વાંચવા અને લખવાની મંજૂરી આપે છે."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"પરવાનગી વપરાશ જુઓને શરૂ કરો"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"કોઈ ઍપ માટે પરવાનગી વપરાશ શરૂ કરવાની ધારકને મંજૂરી આપે છે. સામાન્ય ઍપ માટે ક્યારેય જરૂર પડી ન શકે."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ઉચ્ચ સેમ્પ્લિંગ રેટ પર સેન્સરનો ડેટા ઍક્સેસ કરો"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ઍપને 200 Hzથી વધુના દરે સેન્સરના ડેટાના નમૂનાની મંજૂરી આપે છે"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"પાસવર્ડ નિયમો સેટ કરો"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"સ્ક્રીન લૉક પાસવર્ડ અને પિનમાં મંજૂર લંબાઈ અને અક્ષરોને નિયંત્રિત કરો."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"સ્ક્રીનને અનલૉક કરવાના પ્રયત્નોનું નિયમન કરો"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS વિનંતીને વીડિઓ કૉલમાં બદલવામાં આવી છે"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS વિનંતીને USSD વિનંતીમાં બદલવામાં આવી છે"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"નવી SS વિનંતીમાં બદલવામાં આવી છે"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"ઑફિસની પ્રોફાઇલ"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"અલર્ટ કરેલ"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"વિસ્તૃત કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 93408be..15bb461 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ऐप को परेशान न करें कॉन्फ़िगरेशन पढ़ने और लिखने देती है."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"देखने की अनुमतियां चालू करें"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"इस्तेमाल करने वाले को किसी ऐप्लिकेशन के लिए अनुमतियों का इस्तेमाल शुरू करने देता है. सामान्य ऐप्लिकेशन के लिए इसकी ज़रूरत कभी नहीं पड़ती."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"सेंसर डेटा को नमूने लेने की तेज़ दर पर ऐक्सेस करें"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"यह अनुमति मिलने पर ऐप्लिकेशन, 200 हर्ट्ज़ से ज़्यादा की दर पर सेंसर डेटा का नमूना ले पाएगा"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करना"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"स्क्रीन लॉक पासवर्ड और पिन की लंबाई और उनमें स्वीकृत वर्णों को नियंत्रित करना."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"स्क्रीन अनलॉक करने के की कोशिशों पर नज़र रखना"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"एसएस कोड चलाने के अनुरोध को वीडियो कॉल में बदला गया"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"एसएस कोड चलाने के अनुरोध को यूएसएसडी कोड चलाने के अनुरोध में बदला गया"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"एसएस कोड चलाने के नए अनुरोध में बदला गया"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"फ़िशिंग से जुड़ी चेतावनी"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"वर्क प्रोफ़ाइल"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"अलर्ट किया गया"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"विस्तार करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 7fa4fe3..6e7c330 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -688,10 +688,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućuje aplikaciji čitanje i pisanje konfiguracije opcije Ne ometaj."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti upotrebu dopuštenja za pregled"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dopušta nositelju pokretanje upotrebe dopuštenja za aplikaciju. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora pri višoj brzini uzorkovanja"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Aplikaciji omogućuje uzorkovanje podataka senzora pri brzini većoj od 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Postavi pravila zaporke"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Upravlja duljinom i znakovima koji su dopušteni u zaporkama i PIN-ovima zaključavanja zaslona."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadziri pokušaje otključavanja zaslona"</string>
@@ -1904,6 +1902,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS zahtjev promijenjen je u videopoziv"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS zahtjev promijenjen je u USSD zahtjev"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Promijenjeno u novi SS zahtjev"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Upozorenje o krađi identiteta"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Radni profil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Upozoreni"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Proširivanje"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index d71c562..d966249 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Az alkalmazás olvashatja és szerkesztheti a „Ne zavarjanak” funkció beállításait."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"engedélyhasználat megtekintésének elindítása"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lehetővé teszi a felhasználó számára, hogy elindítsa az alkalmazás engedélyhasználatát. A normál alkalmazásoknak erre soha nincs szükségük."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"hozzáférés a szenzoradatokhoz nagy mintavételezési gyakorisággal"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Lehetővé teszi az alkalmazás számára, hogy 200 Hz-nél magasabb gyakorisággal vegyen mintát a szenzoradatokból"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Jelszavakkal kapcsolatos szabályok beállítása"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"A képernyőzár jelszavaiban és PIN kódjaiban engedélyezett karakterek és hosszúság vezérlése."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Képernyőzár-feloldási kísérletek figyelése"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Az SS-kérés módosítva videohívásra"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Az SS-kérés módosítva USSD-kérésre"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Új SS-kérésre módosítva"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Munkaprofil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Értesítve"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Kibontás"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index e5267d9..6110be3 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Թույլ է տալիս հավելվածին փոփոխել «Չանհանգստացնել» գործառույթի կազմաձևումը:"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"թույլտվությունների մասին տվյալների հասանելիություն"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Հավելվածին հասանելի կդառնան թույլտվությունների մասին տվյալները։ Այս թույլտվությունն անհրաժեշտ չէ սովորական հավելվածներին։"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"օգտագործել սենսորների տվյալները բարձր հաճախականության վրա"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Թույլ է տալիս հավելվածին փորձել սենսորների տվյալները 200 Հց-ից ավել հաճախականության վրա"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Սահմանել գաղտնաբառի կանոնները"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Կառավարել էկրանի ապակողպման գաղտնաբառերի և PIN կոդերի թույլատրելի երկարությունն ու գրանշանները:"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Վերահսկել էկրանի ապակողպման փորձերը"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS հարցումը փոխվել է տեսազանգի"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS հարցումը փոխվել է USSD հարցման"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Փոխվել է նոր SS հարցման"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Աշխատանքային պրոֆիլ"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Ուղարկվել է զգուշացում"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Ընդարձակել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 636bb04..3335f59 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Mengizinkan aplikasi membaca dan menulis konfigurasi status Jangan Ganggu."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulai melihat penggunaan izin"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Memungkinkan pemegang memulai penggunaan izin untuk aplikasi. Tidak diperlukan untuk aplikasi normal."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mengakses data sensor pada frekuensi pengambilan sampel yang tinggi"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Mengizinkan aplikasi mengambil sampel data sensor pada frekuensi yang lebih besar dari 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Setel aturan sandi"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Mengontrol panjang dan karakter yang diizinkan dalam sandi dan PIN kunci layar."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau upaya pembukaan kunci layar"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Permintaan SS diubah ke panggilan video"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Permintaan SS diubah ke permintaan USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Diubah ke permintaan SS baru"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil kerja"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Diingatkan"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Luaskan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index b359e7d..86a4638 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leyfir forriti að lesa og skrifa í grunnstillingu „Ónáðið ekki“."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"heimildanotkun upphafsyfirlits"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leyfir handhafa að byrja heimildanotkun fyrir forrit. Ætti aldrei að þurfa fyrir venjuleg forrit."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"aðgangur að skynjaragögnum með hárri upptökutíðni"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Leyfir forritinu að nota upptökutíðni yfir 200 Hz fyrir skynjaragögn"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Setja reglur um aðgangsorð"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Stjórna lengd og fjölda stafa í aðgangsorðum og PIN-númerum skjáláss."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Fylgjast með tilraunum til að taka skjáinn úr lás"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-beiðni breytt í myndsímtal"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-beiðni breytt í USSD-beiðni"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Breytt í nýja SS-beiðni"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Vinnusnið"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Tilkynnt"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Stækka"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index d4c6ae0..fa6b461 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Consente all\'app di leggere e modificare la configurazione della funzione Non disturbare."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"avvio dell\'uso dell\'autorizzazione di visualizzazione"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Consente al titolare di avviare l\'uso delle autorizzazioni per un\'app. Non dovrebbe essere mai necessaria per le normali applicazioni."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Accesso ai dati dei sensori a una frequenza di campionamento elevata"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Consente all\'app di campionare i dati dei sensori a una frequenza maggiore di 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Impostare regole per le password"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlla la lunghezza e i caratteri ammessi nelle password e nei PIN del blocco schermo."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorare tentativi di sblocco dello schermo"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Richiesta SS modificata in videochiamata"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Richiesta SS modificata in richiesta USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Modificata in nuova richiesta SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profilo di lavoro"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Avviso inviato"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Espandi"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 33f43af..d9b889e 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -691,10 +691,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"מאפשר לאפליקציה לקרוא ולכתוב את התצורה של \'נא לא להפריע\'."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"התחלת צפייה בהרשאות השימוש"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"מאפשרת לבעלים להפעיל את השימוש בהרשאות עבור אפליקציה מסוימת. הרשאה זו אף פעם לא נדרשת עבור אפליקציות רגילות."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"גישה לנתוני חיישנים בתדירות דגימה גבוהה"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"האפליקציה תוכל לדגום נתוני חיישנים בתדירות של מעל 200 הרץ"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"הגדר כללי סיסמה"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"קביעת האורך הנדרש והתווים המותרים בסיסמאות ובקודי הגישה של מסך הנעילה."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"מעקב אחר ניסיונות לביטול של נעילת המסך"</string>
@@ -1935,6 +1933,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"בקשת SS שונתה לשיחת וידאו"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"בקשת SS שונתה לבקשת USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"היה שינוי לבקשת SS חדשה"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"פרופיל עבודה"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"נשלחה התראה"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"הרחב"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 2482c7e..68213def 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS リクエストはビデオ通話に変更されました"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS リクエストは USSD リクエストに変更されました"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"新しい SS リクエストに変更されました"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"フィッシングに関する警告"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"仕事用プロファイル"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"アラートとして送信済み"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"展開"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index e45b18a..3c76454 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1871,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS მოთხოვნა შეიცვალა ვიდეოზარის მოთხოვნით"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS მოთხოვნა შეიცვალა USSD მოთხოვნით"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"შეიცვალა ახალი SS მოთხოვნით"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"სამსახურის პროფილი"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"გაფრთხილებით"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"გაშლა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 08a2e67..38f9475 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Қолданбаға «Мазаламау» конфигурациясын оқу және жазу мүмкіндігін береді."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"рұқсаттарды пайдалану туралы деректерді көру"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Пайдаланушы қолданбаға берілетін рұқсаттарды басқара алады. Ондай рұқсаттар әдеттегі қолданбаларға керек емес."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"жоғары дискретизация жиілігіндегі датчик деректерін пайдалану"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Қолданбаға жиілігі 200 Гц-тен жоғары датчик деректерінің үлгісін таңдауға рұқсат береді."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Құпия сөз ережелерін тағайындау"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Экран бекітпесінің құпия сөздерінің және PIN кодтарының ұзындығын және оларда рұқсат етілген таңбаларды басқару."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Экран құлпын ашу әркеттерін бақылау"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS сұрауы бейне қоңырауға өзгертілді"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS сұрауы USSD сұрауына өзгертілді"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Жаңа SS сұрауына өзгертілді"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Жұмыс профилі"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Ескертілді"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Жаю"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 34e249e..5bde733 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"អនុញ្ញាតឲ្យកម្មវិធីអាន និងសរសេរការកំណត់រចនាសម្ព័ន្ធមុខងារ កុំរំខាន។"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ចាប់ផ្ដើមមើលការប្រើប្រាស់ការអនុញ្ញាត"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"អនុញ្ញាតឱ្យម្ចាស់ចាប់ផ្ដើមការប្រើប្រាស់ការអនុញ្ញាតសម្រាប់កម្មវិធី។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ចូលប្រើទិន្នន័យឧបករណ៍ចាប់សញ្ញានៅអត្រាសំណាកខ្ពស់"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"អនុញ្ញាតឱ្យកម្មវិធីធ្វើសំណាកទិន្នន័យឧបករណ៍ចាប់សញ្ញានៅអត្រាលើសពី 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"កំណត់ក្បួនពាក្យសម្ងាត់"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"គ្រប់គ្រងប្រវែង និងតួអក្សរដែលអនុញ្ញាតឲ្យប្រើក្នុងពាក្យសម្ងាត់ និងលេខសម្ងាត់ចាក់សោអេក្រង់។"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"តាមដានការព្យាយាមដោះសោអេក្រង់"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"សំណើ SS ត្រូវបានប្ដូរទៅការហៅជាវីដេអូ"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"សំណើ SS ត្រូវបានប្ដូរទៅសំណើ USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"បានប្ដូរទៅសំណើ SS ថ្មី"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ការជូនដំណឹងអំពីការដាក់នុយ"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"ប្រវត្តិរូបការងារ"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"បានជូនដំណឹង"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ពង្រីក"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 9a33f06..cf0f644 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ಓದಲು ಮತ್ತು ಬರೆಯಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ವೀಕ್ಷಣಾ ಅನುಮತಿಯ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ಆ್ಯಪ್ಗಾಗಿ ಅನುಮತಿ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಲು ಹೊಂದಿರುವವರಿಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸಾಮಾನ್ಯ ಆ್ಯಪ್ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ಹೆಚ್ಚಿನ ನಮೂನೆ ದರದಲ್ಲಿ ಸೆನ್ಸಾರ್ ಡೇಟಾ ಪ್ರವೇಶಿಸಿ"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz ಗಿಂತಲೂ ಹೆಚ್ಚಿನ ವೇಗದಲ್ಲಿ ಸೆನ್ಸಾರ್ ಡೇಟಾದ ಮಾದರಿ ಪರೀಕ್ಷಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"ಪಾಸ್ವರ್ಡ್ ನಿಮಯಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"ಪರದೆ ಲಾಕ್ನಲ್ಲಿನ ಪಾಸ್ವರ್ಡ್ಗಳು ಮತ್ತು ಪಿನ್ಗಳ ಅನುಮತಿಸಲಾದ ಅಕ್ಷರಗಳ ಪ್ರಮಾಣವನ್ನು ನಿಯಂತ್ರಿಸಿ."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ಪರದೆಯ ಅನ್ಲಾಕ್ ಪ್ರಯತ್ನಗಳನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS ವಿನಂತಿಯನ್ನು ವೀಡಿಯೊ ಕರೆಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS ವಿನಂತಿಯನ್ನು USSD ವಿನಂತಿಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ಹೊಸ SS ವಿನಂತಿಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"ಎಚ್ಚರಿಕೆ ನೀಡಲಾಗಿದೆ"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index a395c89..8fde34c 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"앱에서 방해 금지 모드 설정을 읽고 작성하도록 허용합니다."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"권한 사용 보기 시작"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"앱의 권한 사용을 시작하려면 보유자를 허용하세요. 일반 앱에는 필요하지 않습니다."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"더 높은 샘플링 레이트로 센서 데이터 액세스"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"앱에서 200Hz보다 빠른 속도로 센서 데이터를 샘플링하도록 허용합니다."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"비밀번호 규칙 설정"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"화면 잠금 비밀번호와 PIN에 허용되는 길이와 문자 수를 제어합니다."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"화면 잠금 해제 시도 모니터링"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS 요청이 화상 통화로 변경됨"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS 요청이 USSD 요청으로 변경됨"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"새 SS 요청으로 변경됨"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"직장 프로필"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"알림 전송됨"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"펼치기"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index f8a800d..aa5d01c 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Колдонмого \"Тынчымды алба\" режиминин конфигурациясын окуу жана жазуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"уруксаттын колдонулушун көрүп баштоо"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Колдонмонун пайдаланылышына уруксат берүүгө мүмкүнчүлүк берет. Кадимки колдонмолорго эч качан талап кылынбашы керек."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"үлгүнү жаздыруу ылдамдыгы жогору болгон сенсор дайындарынын үлгүсүнө мүмкүнчүлүк алуу"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Колдонмолорго сенсор дайындарынын үлгүсү 200 Герцтен жогору болгон үлгүлөрдү алууга уруксат берет"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Сырсөз эрежелерин коюу"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Экран кулпусунун сырсөздөрү менен PIN\'дерине уруксат берилген узундук менен белгилерди көзөмөлдөө."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Экран кулпусун ачуу аракеттерин көзөмөлдөө"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS сурамы видео чалууга өзгөртүлдү"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS сурамы USSD сурамына өзгөртүлдү"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Жаңы SS сурамына өзгөртүлдү"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Жумуш профили"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Эскертилди"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Жайып көрсөтүү"</string>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 9e87a47..42c2c69 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -78,4 +78,5 @@
<dimen name="chooser_preview_width">480dp</dimen>
+ <dimen name="toast_y_offset">24dp</dimen>
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 93f3889..806a5e2 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"ປ່ຽນການຮ້ອງຂໍ SS ເປັນການໂທວິດີໂອແລ້ວ"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"ປ່ຽນຄຳຂໍ SS ເປັນຄຳຂໍ USSD ແລ້ວ"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ປ່ຽນຄຳຂໍ SS ໃໝ່ແລ້ວ"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ການແຈ້ງເຕືອນການຫຼອກເອົາຂໍ້ມູນ"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"ເຕືອນແລ້ວ"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ຂະຫຍາຍ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 9c1e09d..8f64bdd 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -691,10 +691,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leidžiama programai skaityti ir rašyti „Do Not Disturb“ konfigūraciją."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pradėti peržiūrėti leidimo naudojimą"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leidžia savininkui pradėti naudoti programos leidimą. Įprastoms programoms to neturėtų prireikti."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pasiekti jutiklių duomenis dideliu skaitmeninimo dažniu"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Programai leidžiama skaitmeninti jutiklių duomenis didesniu nei 200 Hz dažniu"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Nustatyti slaptažodžio taisykles"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Valdykite, kokio ilgio ekrano užrakto slaptažodžius ir PIN kodus galima naudoti."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Stebėti bandymus atrakinti ekraną"</string>
@@ -1935,6 +1933,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS užklausa pakeista į vaizdo skambutį"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS užklausa pakeista į USSD užklausą"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Pakeista į naują SS užklausą"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Darbo profilis"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Įspėjimas išsiųstas"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Išskleisti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index d77cfd4..346ade4 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -688,10 +688,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ļauj lietotnei lasīt un rakstīt režīma “Netraucēt” konfigurāciju."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Datu skatīšana par izmantojamajām atļaujām"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ļauj atļaujas īpašniekam sākt lietotnes atļauju izmantošanu. Parastām lietotnēm tas nekad nav nepieciešams."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"piekļuve sensoru datiem, izmantojot augstu iztveršanas frekvenci"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Ļauj lietotnei iztvert sensoru datus, izmantojot frekvenci, kas ir augstāka par 200 Hz."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Paroles kārtulu iestatīšana"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolēt ekrāna bloķēšanas paroļu un PIN garumu un tajos atļautās rakstzīmes."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekrāna atbloķēšanas mēģinājumu pārraudzīšana"</string>
@@ -1904,6 +1902,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS pieprasījums mainīts uz videozvanu"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS pieprasījums mainīts uz USSD pieprasījumu"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Mainīts uz jaunu SS pieprasījumu"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Darba profils"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Brīdināts"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Izvērst"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 54ac67b..8d176ad 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозволува апликацијата да чита и пишува конфигурација Не вознемирувај."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"започнете со користење на дозволата за приказ"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозволува сопственикот да почне со користење на дозволата за апликација. Не треба да се користи за стандардни апликации."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"пристапува до податоците со висока фреквенција на семпл"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Дозволува апликацијата да пристапува до податоците од сензорите со фреквенција на семпл поголема од 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Постави правила за лозинката"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролирај ги должината и знаците што се дозволени за лозинки и PIN-броеви за отклучување екран."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Следи ги обидите за отклучување на екранот"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Барањето SS е изменето во видео повик"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Барањето SS е изменето во барање USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Променето на ново барање SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Работен профил"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Предупредено"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Прошири"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 1329fb0..fd61768 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1873,6 +1873,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS അഭ്യർത്ഥന, വീഡിയോ കോളിലേക്ക് മാറ്റി"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS അഭ്യർത്ഥന, USSD അഭ്യർത്ഥനയിലേക്ക് മാറ്റി"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"പുതിയ SS അഭ്യർത്ഥനയിലേക്ക് മാറ്റി"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"മുന്നറിയിപ്പ് നൽകി"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"വികസിപ്പിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 4b1dbb3..d5937d3 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1871,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS хүсэлтийг видео дуудлага болгон өөрчилсөн"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS хүсэлтийг USSD хүсэлт болгон өөрчилсөн"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Шинэ SS хүсэлт болгон өөрчилсөн"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Ажлын профайл"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Мэдэгдсэн"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Дэлгэх"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index ea22290..5f4d5fd 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1873,6 +1873,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS विनंती व्हिडिओ कॉलवर बदलली"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS विनंती USSD विनंतीवर बदलली"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"नवीन SS विनंतीवर बदलली"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"कार्य प्रोफाईल"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"सूचना दिल्या"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"विस्तृत करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 0a1205f..27b1d60 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Membenarkan apl membaca dan menulis konfigurasi Jangan Ganggu."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulakan lihat penggunaan kebenaran"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Membenarkan pemegang memulakan penggunaan kebenaran untuk apl. Tidak sekali-kali diperlukan untuk apl biasa."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"akses data penderia pada data pensampelan yang tinggi"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Membenarkan apl mengambil sampel data penderia pada kadar yang lebih besar daripada 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Tetapkan peraturan kata laluan"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Mengawal panjang dan aksara yang dibenarkan dalam kata laluan dan PIN kunci skrin."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau percubaan buka kunci skrin"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Permintaan SS ditukar kepada panggilan video"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Permintaan SS ditukar kepada permintaan USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Bertukar kepada permintaan SS baharu"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Amaran pancingan data"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil kerja"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Dimaklumkan"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Kembangkan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 845c24f..9e1bd73 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -649,8 +649,8 @@
<string name="permdesc_bind_incall_service" msgid="4124917526967765162">"အက်ပ်အား အသုံးပြုသူက ခေါ်ဆိုမှုအဝင် မျက်နှာပြင် ဘယ်အချိန်မှာ ဘယ်လို မြင်ရမှာကို ထိန်းချုပ်ခွင့်ပေးရန်"</string>
<string name="permlab_bind_connection_service" msgid="5409268245525024736">"တယ်လီဖုန်း ဝန်ဆောင်မှုများနှင့် အပြန်အလှန် တုံ့ပြန်မှု"</string>
<string name="permdesc_bind_connection_service" msgid="6261796725253264518">"အက်ပ်အား ခေါ်ဆိုမှုများ လုပ်ခြင်း/လက်ခံခြင်း ပြုလုပ်နိုင်ရန် တယ်လီဖုန်း ဝန်ဆောင်မှုများနှင့် အပြန်အလှန် တုံ့ပြန်မှုကို ခွင့်ပြုသည်။"</string>
- <string name="permlab_control_incall_experience" msgid="6436863486094352987">"အသုံးပြုသူ အတွက် ခေါ်ဆိုမှုအဝင် လုပ်ကိုင်ပုံကို စီစဉ်ပေးခြင်း"</string>
- <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"အက်ပ်အား အသုံးပြုသူ အတွက် ခေါ်ဆိုမှုအဝင် လုပ်ကိုင်ပုံကို စီစဉ်ခွင့် ပြုသည်။"</string>
+ <string name="permlab_control_incall_experience" msgid="6436863486094352987">"အဝင်ခေါ်ဆိုမှုအတွက် အသုံးပြုသူ၏ နှစ်သက်မှုကို ခွင့်ပြုခြင်း"</string>
+ <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"အဝင်ခေါ်ဆိုမှုအတွက် အသုံးပြုသူ၏ နှစ်သက်မှုကို ပံ့ပိုးပေးရန် အက်ပ်အား ခွင့်ပြုသည်။"</string>
<string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"ရာဇဝင်အလိုက် ကွန်ယက်သုံစွဲမှုအား ဖတ်ခြင်း"</string>
<string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"အက်ပ်အား အထူး ကွန်ရက်များ နှင့် အက်ပ်များ အတွက် ကွန်ရက် အသုံးပြုမှု မှတ်တမ်းကို ဖတ်ကြားခွင့် ပြုသည်။"</string>
<string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"ကွန်ယက်မူဝါဒအား စီမံခြင်း"</string>
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"မနှောင့်ယှက်ရန် ချိန်ညှိမှုကို အပ်ဖ်များ ဖတ်ခြင်း ပြင်ခြင်းပြုလုပ်နိုင်ရန် ခွင့်ပြုမည်။"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"အစမြင်ကွင်း ခွင့်ပြုချက် အသုံးပြုမှု"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"အက်ပ်တစ်ခုအတွက် ခွင့်ပြုချက်စတင်အသုံးပြုမှုကို ကိုင်ဆောင်သူအား ခွင့်ပြုသည်။ ပုံမှန်အက်ပ်များအတွက် ဘယ်သောအခါမျှ မလိုအပ်ပါ။"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"နမူနာနှုန်းမြင့်သော အာရုံခံစနစ်ဒေတာကို သုံးပါ"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"၂၀၀ Hz နှုန်းထက်ပိုများသော အာရုံခံစနစ်ဒေတာကို နမူနာယူရန် အက်ပ်အား ခွင့်ပြုပါ"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"စကားဝှက်စည်းမျဥ်းကိုသတ်မှတ်ရန်"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"မျက်နှာပြင်သော့ခတ်သည့် စကားဝှက်များနှင့် PINများရှိ ခွင့်ပြုထားသည့် စာလုံးအရေအတွက်နှင့် အက္ခရာများအား ထိန်းချုပ်ရန်။"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"မျက်နှာပြင်လော့ခ်ဖွင့်ရန် ကြိုးပမ်းမှုများကို စောင့်ကြည့်ပါ"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS တောင်းဆိုမှုကို ဗီဒီယိုခေါ်ဆိုမှုသို့ ပြောင်းထားသည်"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS တောင်းဆိုမှုကို USSD တောင်းဆိုမှုအဖြစ် ပြောင်းထားသည်"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"SS တောင်းဆိုမှုအသစ်သို့ ပြောင်းထားသည်"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"အယောင်ဆောင်ဖြားယောင်းခြင်း သတိပေးချက်"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"အလုပ်ကိုယ်ရေးအချက်အလက်"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"သတိပေးထားသည်"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ချဲ့ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 4e05659..c846701 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Lar appen lese og skrive konfigurasjon av Ikke forstyrr."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start visning av bruk av tillatelser"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lar innehaveren starte bruk av tillatelser for en app. Dette skal aldri være nødvendig for vanlige apper."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"tilgang til sensordata ved høy samplingfrekvens"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Lar appen samle inn sensordata ved en hastighet som er høyere enn 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Angi passordregler"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrollerer tillatt lengde og tillatte tegn i passord og PIN-koder for opplåsing av skjermen."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåk forsøk på å låse opp skjermen"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-forespørsel endret til videoanrop"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-forespørsel endret til USSD-forespørsel"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Endret til ny SS-forespørsel"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Arbeidsprofil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Varslet"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Vis"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index f76011a..9af472a 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1873,6 +1873,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS अनुरोधलाई भिडियो कलमा परिवर्तन गरियो"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS अनुरोधलाई USSD अनुरोधमा परिवर्तन गरियो"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"नयाँ SS अनुरोधमा परिवर्तन गरियो"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"कार्य प्रोफाइल"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"सतर्कता गरियो"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"विस्तृत गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 01d4a70..d76d32a 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Hiermee kan de app configuratie voor Niet storen lezen en schrijven."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rechtengebruik starten"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Hiermee kan de houder het rechtengebruik voor een app starten. Nooit vereist voor normale apps."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"toegang krijgen tot sensorgegevens met een hoge samplingsnelheid"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Hiermee kan de app sensorgegevens samplen met een snelheid die hoger is dan 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Wachtwoordregels instellen"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"De lengte en het aantal tekens beheren die zijn toegestaan in wachtwoorden en pincodes voor schermvergrendeling."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pogingen voor schermontgrendeling bijhouden"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-verzoek gewijzigd in videogesprek"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-verzoek gewijzigd in USSD-verzoek"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Gewijzigd in nieuw SS-verzoek"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishingmelding"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Werkprofiel"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Gemeld"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Uitvouwen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index e145787..f42d8ab 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" କନଫିଗରେଶନ୍ ପଢ଼ିବା ତଥା ଲେଖିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ଅନୁମତି ବ୍ୟବହାର ଦେଖିବା ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ଏକ ଆପ୍ ପାଇଁ ଅନୁମତିର ବ୍ୟବହାର ଆରମ୍ଭ କରିବାକୁ ଧାରକକୁ ଅନୁମତି ଦେଇଥାଏ। ସାଧାରଣ ଆପ୍ଗୁଡ଼ିକ ପାଇଁ ଏହା ଆବଶ୍ୟକ ନୁହେଁ।"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ଏକ ଉଚ୍ଚ ନମୁନାକରଣ ରେଟରେ ସେନ୍ସର୍ ଡାଟାକୁ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz ଠାରୁ ଅଧିକ ଏକ ରେଟରେ ସେନ୍ସର୍ ଡାଟାର ନମୁନା ନେବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"ପାସ୍ୱର୍ଡ ନିୟମାବଳୀ ସେଟ୍ କରନ୍ତୁ"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"ଲକ୍ ସ୍କ୍ରୀନ୍ ପାସ୍ୱର୍ଡ ଓ PINରେ ଅନୁମୋଦିତ ଦୀର୍ଘତା ଓ ବର୍ଣ୍ଣ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ।"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ସ୍କ୍ରୀନ୍-ଅନଲକ୍ କରିବା ଉଦ୍ୟମ ନୀରିକ୍ଷଣ କରନ୍ତୁ"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SSଙ୍କ ଅନୁରୋଧକୁ ଭିଡିଓ କଲ୍ରେ ପରିବର୍ତ୍ତନ କରାଗଲା"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS ଅନୁରୋଧ, USSD ଅନୁରୋଧକୁ ପରିବର୍ତ୍ତନ ହେଲା"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ନୂତନ SS ଅନୁରୋଧରେ ପରିବର୍ତ୍ତନ ହେଲା"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"ଆଲର୍ଟ କରାଯାଇଛି"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ବଢ଼ାନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index c51845b..17a4944 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1873,6 +1873,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS ਬੇਨਤੀ ਨੂੰ ਵੀਡੀਓ ਕਾਲ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS ਬੇਨਤੀ ਨੂੰ USSD ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ਨਵੀਂ SS ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"ਸੁਚੇਤਨਾਵਾਂ"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ਵਿਸਤਾਰ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 6f54b41..14ca2b9 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -691,10 +691,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Pozwala aplikacji na odczyt i zmianę konfiguracji trybu Nie przeszkadzać."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rozpocząć wyświetlanie użycia uprawnień"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umożliwia rozpoczęcie korzystania z uprawnienia dotyczącego danej aplikacji jego posiadaczowi. Zwykłe aplikacje nie powinny potrzebować tego uprawnienia."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"dostęp do danych czujnika z wysoką częstotliwością"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Zezwala aplikacji na pobieranie próbek danych z czujnika z częstotliwością wyższą niż 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Określ reguły hasła"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolowanie długości haseł blokady ekranu i kodów PIN oraz dozwolonych w nich znaków."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorowanie prób odblokowania ekranu"</string>
@@ -1935,6 +1933,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Żądanie SS zmienione na rozmowę wideo"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Żądanie SS zmienione na żądanie USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Zmieniono na nowe żądanie SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil służbowy"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alert"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Rozwiń"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index bfab1f3..3fbfd7e 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Solicitação SS alterada para videochamada"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Solicitação SS alterada para solicitação USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Alterada para uma nova solicitação SS"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de phishing"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabalho"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alertado"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expandir"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index ee18a2f..eb09af8 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite à app ler e alterar a configuração de Não incomodar"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar utilização da autorização de visualização"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o titular inicie a utilização de autorizações para uma app. Nunca deverá ser necessário para aplicações normais."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"aceder aos dados de sensores a uma taxa de amostragem elevada"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que a app obtenha uma amostra dos dados de sensores a uma taxa superior a 200 Hz."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras de palavra-passe"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlar o comprimento e os carateres permitidos nos PINs e nas palavras-passe do bloqueio de ecrã."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorizar tentativas de desbloqueio do ecrã"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"O pedido SS foi alterado para uma videochamada."</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"O pedido SS foi alterado para um novo pedido USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Foi alterado para um novo pedido SS."</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabalho"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alertado"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expandir"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index bfab1f3..bad102e 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1871,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Solicitação SS alterada para videochamada"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Solicitação SS alterada para solicitação USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Alterada para uma nova solicitação SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabalho"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Alertado"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expandir"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 9e6b3f9..1e789e4 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -688,10 +688,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite aplicației să citească și să scrie configurația Nu deranja."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"porniți folosirea permisiunii de vizualizare"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite proprietarului să pornească folosirea permisiunii pentru o aplicație. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"să acceseze date de la senzori la o rată de eșantionare mare"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite aplicației să colecteze date de la senzori la o rată de eșantionare de peste 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Să seteze reguli pentru parolă"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Stabiliți lungimea și tipul de caractere permise pentru parolele și codurile PIN de blocare a ecranului."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Să monitorizeze încercările de deblocare a ecranului"</string>
@@ -1904,6 +1902,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Solicitarea SS a fost schimbată cu un apel video"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Solicitarea SS a fost schimbată cu o solicitare USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Schimbat cu o solicitare SS nouă"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alertă privind phishingul"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil de serviciu"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Notificat"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Extindeți"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index c237709..5cb8a50 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -691,10 +691,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Открывает приложению доступ к настройкам режима \"Не беспокоить\" и позволяет изменять их."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Просмотр данных об используемых разрешениях"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Приложение получит доступ к данным об используемых разрешениях. Это разрешение не требуется обычным приложениям."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Доступ к данным датчиков при высокой частоте дискретизации"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Приложение сможет считывать данные датчиков на частоте более 200 Гц."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Настройка правил для паролей"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролировать длину и символы при вводе пароля и PIN-кода."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Отслеживание попыток разблокировать экран"</string>
@@ -1935,6 +1933,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-запрос преобразован в видеовызов"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-запрос преобразован в USSD-запрос"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Преобразовано в SS-запрос"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Рабочий профиль"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Отправлено оповещение"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Развернуть"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 5a00e92..bd311fb9 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS ඉල්ලීම වීඩියෝ ඇමතුමට වෙනස් කරන ලදී"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS ඉල්ලීම USSD ඉල්ලීමට වෙනස් කරන ලදී"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"නව SS ඉල්ලීමට වෙනස් කරන ලදී"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"තතුබෑම් ඇඟවීම"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"කාර්යාල පැතිකඩ"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"අනතුරු අඟවන ලදී"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"දිග හරින්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index de7a2a4..825215b 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -691,10 +691,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikácii čítať a zapisovať konfiguráciu režimu bez vyrušení."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"spustenie používania povolenia na zobrazenie"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje držiteľovi spustiť používanie povolenia aplikáciou. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"prístup k dátam senzorom s vysokou vzorkovacou frekvenciou"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Umožňuje aplikácii vzorkovať dáta senzorov s frekvenciou vyššou ako 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Nastaviť pravidlá pre heslo"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Nastavte dĺžku hesiel na odomknutie obrazovky aj kódov PIN a v nich používané znaky."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovanie pokusov o odomknutie obrazovky"</string>
@@ -1935,6 +1933,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Žiadosť SS bola zmenená na videohovor"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Žiadosť SS bola zmenená na žiadosť USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Zmenené na novú žiadosť SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Pracovný profil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Upozornené"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Rozbaliť"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 3575131..a27b805 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -691,10 +691,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Aplikaciji omogoča branje in pisanje konfiguracije načina »ne moti«."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"začetek uporabe dovoljenja za ogledovanje"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Imetniku omogoča začetek uporabe dovoljenj za aplikacijo. Nikoli ni potrebno za navadne aplikacije."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"dostop do podatkov tipal z večjo hitrostjo vzorčenja"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Aplikaciji dovoljuje, da vzorči podatke tipal s hitrostjo, večjo od 200 Hz."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Nastavitev pravil za geslo"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Nadzor nad dolžino in znaki, ki so dovoljeni v geslih in kodah PIN za odklepanje zaslona."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadzor nad poskusi odklepanja zaslona"</string>
@@ -1935,6 +1933,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Zahteva SS je spremenjena v videoklic"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Zahteva SS je spremenjena v zahtevo USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Spremenjeno v novo zahtevo SS"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Opozorilo o lažnem predstavljanju"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Delovni profil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Opozorilo prikazano"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Razširi"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 8857e08..823b963 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1871,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Kërkesa SS u ndryshua në telefonatë me video"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Kërkesa SS u ndryshua në kërkesë USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"U ndryshua në kërkesë të re SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profili i punës"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Sinjalizuar"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Zgjero"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 4aca200..74589a8 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -688,10 +688,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозвољава апликацији да чита и уписује конфигурацију подешавања Не узнемиравај."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"почетак коришћења дозволе за преглед"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозвољава власнику да започне коришћење дозволе за апликацију. Никада не би требало да буде потребна за уобичајене апликације."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"приступ подацима сензора при великој брзини узорковања"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Дозвољава апликацији да узима узорак података сензора при брзини већој од 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Подешавање правила за лозинку"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролише дужину и знакове дозвољене у лозинкама и PIN-овима за закључавање екрана."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Надгледајте покушаје откључавања екрана"</string>
@@ -1904,6 +1902,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS захтев је промењен у видео позив"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS захтев је промењен у USSD захтев"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Промењено је у нови SS захтев"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Упозорење о „пецању“"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Пословни профил"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Обавештено"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Прошири"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index fb6d4ce..47bfe0a 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ger appen läs- och skrivbehörighet till konfigurationen för Stör ej."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"börja visa behörighetsanvändningen"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Gör att innehavaren kan öppna behörighetsanvändning för en app. Ska inte behövas för vanliga appar."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"åtkomst till sensordata med en hög samplingsfrekvens"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Tillåter att appen får åtkomst till sensordata med en högre samplingsfrekvens än 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Ange lösenordsregler"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Styr tillåten längd och tillåtna tecken i lösenord och pinkoder för skärmlåset."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Övervaka försök att låsa upp skärmen"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-begäran har ändrats till videosamtal"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-begäran har ändrats till en USSD-begäran"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Har ändrats till ny SS-begäran"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Jobbprofil"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Aviserad"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Utöka"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 93dc3b9..fcc9c9d 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1871,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Imebadilisha ombi la SS kuwa simu ya video"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Imebadilisha ombi la SS kuwa ombi la USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Imebadilisha kuwa ombi jipya la SS"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Arifa ya wizi wa data binafsi"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Wasifu wa kazini"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Imearifu"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Panua"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index e70ffec..535fb6c 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"தொந்தரவு செய்ய வேண்டாம் உள்ளமைவைப் படிக்கவும் எழுதவும், ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"அனுமதி உபயோகத்தை அணுகுதல்"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ஆப்ஸிற்கான அனுமதி உபயோகத்தை ஹோல்டருக்கு வழங்கும். இயல்பான ஆப்ஸிற்கு இது எப்போதுமே தேவைப்படாது."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"அதிகளவிலான சாம்பிளிங் ரேட்டில் சென்சார் தரவை அணுகுதல்"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 ஹெர்ட்ஸ்க்கும் அதிகமான வீதத்தில் சென்சார் தரவை மாதிரியாக்க ஆப்ஸை அனுமதிக்கும்"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"கடவுச்சொல் விதிகளை அமைக்கவும்"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"திரைப் பூட்டின் கடவுச்சொற்கள் மற்றும் பின்களில் அனுமதிக்கப்படும் நீளத்தையும் எழுத்துக்குறிகளையும் கட்டுப்படுத்தும்."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"திரையைத் திறப்பதற்கான முயற்சிகளைக் கண்காணி"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS கோரிக்கை, வீடியோ அழைப்பிற்கு மாற்றப்பட்டது"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS கோரிக்கை, USSD கோரிக்கைக்கு மாற்றப்பட்டது"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"புதிய SS கோரிக்கைக்கு மாற்றப்பட்டது"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ஃபிஷிங் எச்சரிக்கை"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"பணிக் கணக்கு"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"விழிப்பூட்டல் ஐகான்"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"விரிவாக்கும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 365e6d7..59d451a 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1873,6 +1873,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS అభ్యర్థన వీడియో కాల్కి మార్చబడింది"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS అభ్యర్థన USSD అభ్యర్థనకు మార్చబడింది"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"కొత్త SS అభ్యర్థనకు మార్చబడింది"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ఫిషింగ్ అలర్ట్"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"కార్యాలయ ప్రొఫైల్"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"హెచ్చరించబడింది"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"విస్తరింపజేయి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b41e89218..cb21c72 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"อนุญาตให้แอปอ่านและเขียนการกำหนดค่าโหมดห้ามรบกวน"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"เริ่มการใช้สิทธิ์การดู"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"อนุญาตให้เจ้าของเริ่มการใช้สิทธิ์ของแอป ไม่จำเป็นสำหรับแอปทั่วไป"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"เข้าถึงข้อมูลเซ็นเซอร์ที่อัตราการสุ่มตัวอย่างสูง"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"อนุญาตให้แอปสุ่มตัวอย่างข้อมูลเซ็นเซอร์ที่อัตราสูงกว่า 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"ตั้งค่ากฎรหัสผ่าน"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"ควบคุมความยาวและอักขระที่สามารถใช้ในรหัสผ่านของการล็อกหน้าจอและ PIN"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"คำขอ SS เปลี่ยนเป็นวิดีโอคอลแล้ว"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"คำขอ SS เปลี่ยนเป็นคำขอ USSD แล้ว"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"เปลี่ยนเป็นคำขอ SS ใหม่แล้ว"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"การแจ้งเตือนฟิชชิง"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"โปรไฟล์งาน"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"แจ้งเตือนแล้ว"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ขยาย"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index c617ccf..6f00ba8 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1871,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Ginawang video call ang SS na kahilingan"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Ginawang USSD na kahilingan ang SS na kahilingan"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Ginawang bagong SS na kahilingan"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profile sa trabaho"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Naalertuhan"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Palawakin"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 5724bcf..67086d2 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1871,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS isteği görüntülü görüşme olarak değişti"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS isteği USSD isteği olarak değişti"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Yeni SS isteği olarak değişti"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"İş profili"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Sesli uyarıldı"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Genişlet"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index ff1936f..d6a3677 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -691,10 +691,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Додаток зможе переглядати та змінювати конфігурацію режиму \"Не турбувати\"."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"перегляньте дані про використання дозволів"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Власник зможе використовувати дозволи для цього додатка. Цей дозвіл не потрібен для звичайних додатків."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"доступ до даних датчиків із високою частотою дикретизації"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Додаток зможе дискретизувати дані даних датчиків із частотою понад 200 Гц"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Устан. правила пароля"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Укажіть максимальну довжину та кількість символів для паролів розблокування екрана та PIN-кодів."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Відстежувати спроби розблокування екрана"</string>
@@ -1935,6 +1933,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Запит SS змінено на відеовиклик"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Запит SS змінено на запит USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Змінено на новий запит SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Робочий профіль"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Зі звуком"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Розгорнути"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index e1fb04c..9a7cb87 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ایپ کو ڈسٹرب نہ کریں کنفیگریشن لکھنے اور پڑھنے کے قابل کرتا ہے۔"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"اجازت کی استعمال کا ملاحظہ شروع کریں"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"حامل کو ایپ کی اجازت کے استعمال کو شروع کرنے کی اجازت دیتا ہے۔ عام ایپس کے لیے کبھی بھی درکار نہیں ہونا چاہیے۔"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"نمونہ کاری کی اعلی شرح پر سینسر کے ڈیٹا تک رسائی حاصل کریں"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ایپ کو Hz200 سے زیادہ شرح پر سینسر ڈیٹا کا نمونہ لینے کی اجازت دیتی ہے"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"پاس ورڈ کے اصول سیٹ کریں"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"اسکرین لاک پاس ورڈز اور PINs میں اجازت یافتہ لمبائی اور حروف کو کنٹرول کریں۔"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"اسکرین غیر مقفل کرنے کی کوششیں مانیٹر کریں"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS درخواست کو ویڈیو کال میں تبدیل کر دیا گیا"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS درخواست کو USSD درخواست میں تبدیل کر دیا گیا"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"نئی SS درخواست میں تبدیل کر دیا گیا"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"فریب دہی کا الرٹ"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"دفتری پروفائل"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"الرٹ کیا گیا"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"پھیلائیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index ddb5b1c..60134a47 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"“Bezovta qilinmasin” rejimi sozlamalarini ko‘rish va o‘zgartirish."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"foydalaniladigan ruxsatlar axborotini ochish"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ilova foydalanadigan ruxsatlar axborotini ishga tushirishga ruxsat beradi. Oddiy ilovalar uchun talab qilinmaydi."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"yuqori diskretlash chastotali sensor axborotiga ruxsat"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Ilova sensor axborotini 200 Hz dan yuqori tezlikda hisoblashi mumkin"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Parol qoidalarini o‘rnatish"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Ekran qulfi paroli va PIN kodlari uchun qo‘yiladigan talablarni (belgilar soni va uzunligi) nazorat qiladi."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranni qulfdan chiqarishga urinishlarni nazorat qilish"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS talabi video chaqiruvga almashtirildi"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS talabi USSD talabiga almashtirildi"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Yangi SS talabiga almashtirildi"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Ish profili"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Ogohlantirildi"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Yoyish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 13ffe30..25f0644 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Cho phép ứng dụng đọc và ghi cấu hình Không làm phiền."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"cấp quyền xem"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Cho phép chủ sở hữu cấp quyền cho một ứng dụng. Các ứng dụng thông thường sẽ không bao giờ cần quyền này."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"truy cập vào dữ liệu cảm biến ở tốc độ lấy mẫu cao"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Cho phép ứng dụng lấy mẫu dữ liệu cảm biến ở tốc độ lớn hơn 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Đặt quy tắc mật khẩu"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kiểm soát độ dài và ký tự được phép trong mật khẩu khóa màn hình và mã PIN."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Giám sát những lần thử mở khóa màn hình"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Yêu cầu SS đã thay đổi thành cuộc gọi video"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Yêu cầu SS đã thay đổi thành yêu cầu USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Đã thay đổi thành yêu cầu SS mới"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Hồ sơ công việc"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Đã phát âm báo"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Mở rộng"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 13fa62a..358367e 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允许此应用读取和写入“勿扰”模式配置。"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"授权使用“查看权限”"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允许该应用开始查看应用的权限使用情况(普通应用绝不需要此权限)。"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高采样率访问传感器数据"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允许应用以高于 200 Hz 的频率对传感器数据进行采样"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"设置密码规则"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"控制锁屏密码和 PIN 码所允许的长度和字符。"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"监控屏幕解锁尝试次数"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS 请求已更改为视频通话"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS 请求已更改为 USSD 请求"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"已更改为新的 SS 请求"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"网上诱骗警报"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"工作资料"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"已提醒"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"展开"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index dd5eada..e4fa2e0 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取和寫入「請勿騷擾」設定。"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"開始查看權限使用情況"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始查看應用程式的權限使用情況 (一般應用程式並不需要)。"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高取樣率存取感應器資料"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允許應用程式以大於 200 Hz 的頻率對感應器資料進行取樣"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"控制螢幕鎖定密碼和 PIN 所允許的長度和字元。"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS 要求已變更為視像通話"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS 要求已變更為 USSD 要求"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"已變更為新的 SS 要求"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"仿冒詐騙警示"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"工作設定檔"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"已提醒"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"展開"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index a257344..49e57b3 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取及寫入「零打擾」設定。"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"啟動檢視權限用途"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始使用其他應用程式 (一般應用程式並不需要)。"</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高取樣率存取感應器資料"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允許應用程式以高於 200 Hz 的頻率對感應器資料進行取樣"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"管理螢幕鎖定密碼和 PIN 碼支援的字元和長度上限。"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string>
@@ -1873,6 +1871,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS 要求已變更為視訊通話"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS 要求已變更為 USSD 要求"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"已變更為新的 SS 要求"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"網路詐騙警示"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"工作資料夾"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"已提醒"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"展開"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 712e578..9a21e78 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -685,10 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ivumela izinhlelo zokusebenza ukufunda nokubhala ukulungiswa kokuthi Ungaphazamisi."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"qala ukusetshenziswa kokubuka imvume"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ivumela umphathi ukuthi aqale ukusetshenziswa kwemvume kohlelo lokusebenza. Akumele idingelwe izinhlelo zokusebenza ezijwayelekile."</string>
- <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
- <skip />
- <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
- <skip />
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"finyelela idatha yenzwa ngenani eliphezulu lokwenza isampuli"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Ivumela uhlelo lokusebenza lusampule idatha yenzwa ngenani elikhulu kuno-200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Misa imithetho yephasiwedi"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Lawula ubude nezinhlamvu ezivunyelwe kumaphasiwedi wokukhiya isikrini nama-PIN."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Qapha imizamo yokuvula isikrini sakho"</string>
@@ -1873,6 +1871,8 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Isicelo se-SS sishintshele kukholi yevidiyo"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Isicelo se-SS sishintshele kusicelo se-USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Ishintshele kusicelo esisha se-SS"</string>
+ <!-- no translation found for notification_phishing_alert_content_description (494227305355958790) -->
+ <skip />
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Iphrofayela yomsebenzi"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Kuxwayisiwe"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Nweba"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 69bb20c..735e1224 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3564,6 +3564,16 @@
<attr name="__removed2" format="boolean" />
<!-- Specifies whether the IME supports showing inline suggestions. -->
<attr name="supportsInlineSuggestions" format="boolean" />
+ <!-- Specify one or more configuration changes that the IME will handle itself. If not
+ specified, the IME will be restarted if any of these configuration changes happen in
+ the system. Otherwise, the IME will remain running and its
+ {@link android.inputmethodservice.InputMethodService#onConfigurationChanged}
+ method is called with the new configuration.
+ <p>Note that all of these configuration changes can impact the
+ resource values seen by the application, so you will generally need
+ to re-retrieve all resources (including view layouts, drawables, etc)
+ to correctly handle any configuration change.-->
+ <attr name="configChanges" />
</declare-styleable>
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
@@ -4117,6 +4127,94 @@
user's time zone. Please refer to {@link java.util.TimeZone} for more
information about time zone ids. -->
<attr name="timeZone" format="string"/>
+ <!-- Tint to apply to the dial graphic. -->
+ <attr name="dialTint" format="color" />
+ <!-- Blending mode used to apply the dial graphic tint. -->
+ <attr name="dialTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
+ <!-- Tint to apply to the hour hand graphic. -->
+ <attr name="hand_hourTint" format="color" />
+ <!-- Blending mode used to apply the hour hand graphic tint. -->
+ <attr name="hand_hourTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
+ <!-- Tint to apply to the minute hand graphic. -->
+ <attr name="hand_minuteTint" format="color" />
+ <!-- Blending mode used to apply the minute hand graphic tint. -->
+ <attr name="hand_minuteTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
+ <!-- Tint to apply to the second hand graphic. -->
+ <attr name="hand_secondTint" format="color" />
+ <!-- Blending mode used to apply the second hand graphic tint. -->
+ <attr name="hand_secondTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
</declare-styleable>
<declare-styleable name="Button">
</declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9a917b7..592a3a1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1173,6 +1173,9 @@
<!-- Default value for LED off time when the battery is low on charge in miliseconds -->
<integer name="config_notificationsBatteryLedOff">2875</integer>
+ <!-- If true, only colorized CallStyle notifications will apply custom colors -->
+ <bool name="config_callNotificationActionColorsRequireColorized">true</bool>
+
<!-- Number of notifications to keep in the notification service historical archive -->
<integer name="config_notificationServiceArchiveSize">100</integer>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 3c712ed..695a831 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -30,7 +30,13 @@
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
- <dimen name="toast_y_offset">24dp</dimen>
+ <!-- Offset from the bottom of the device a toast shows -->
+ <dimen name="toast_y_offset">48dp</dimen>
+ <!-- Max width of a toast -->
+ <dimen name="toast_width">300dp</dimen>
+ <!-- Text size of the message within a toast -->
+ <dimen name="toast_text_size">14sp</dimen>
+
<!-- Height of the status bar -->
<dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen>
<!-- Height of the status bar in portrait. The height should be
@@ -265,11 +271,8 @@
<!-- The top margin before the notification progress bar. -->
<dimen name="notification_progress_margin_top">8dp</dimen>
- <!-- height of the notification header when the notification is alone (minimized / groups) -->
- <dimen name="notification_header_solo_height">48dp</dimen>
-
- <!-- height of the notification header when in a "big" layout -->
- <dimen name="notification_header_big_height">56dp</dimen>
+ <!-- height of the notification header -->
+ <dimen name="notification_header_height">56dp</dimen>
<!-- The height of the background for a notification header on a group -->
<dimen name="notification_header_background_height">49.5dp</dimen>
@@ -359,7 +362,7 @@
<dimen name="media_notification_expanded_image_margin_bottom">20dp</dimen>
<!-- The absolute height for the header in a media notification. -->
- <dimen name="media_notification_header_height">@dimen/notification_header_big_height</dimen>
+ <dimen name="media_notification_header_height">@dimen/notification_header_height</dimen>
<!-- The margin of the content to an image-->
<dimen name="notification_content_image_margin_end">8dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a1ea61c..4732e5f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3076,6 +3076,14 @@
<public name="maxResizeHeight" />
<public name="targetCellWidth" />
<public name="targetCellHeight" />
+ <public name="dialTint"/>
+ <public name="dialTintMode"/>
+ <public name="hand_hourTint"/>
+ <public name="hand_hourTintMode"/>
+ <public name="hand_minuteTint"/>
+ <public name="hand_minuteTintMode"/>
+ <public name="hand_secondTint"/>
+ <public name="hand_secondTintMode"/>
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 24afe07..c7ded0c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -964,8 +964,9 @@
</style>
<style name="TextAppearance.Toast">
- <item name="fontFamily">sans-serif</item>
+ <item name="fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="textSize">14sp</item>
+ <item name="textColor">?android:attr/textColorPrimary</item>
</style>
<style name="TextAppearance.Tooltip">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b5af524..29b8e6e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2286,6 +2286,7 @@
<java-symbol type="string" name="config_wifi_tether_enable" />
<java-symbol type="bool" name="config_intrusiveNotificationLed" />
<java-symbol type="bool" name="config_notificationBadging" />
+ <java-symbol type="bool" name="config_callNotificationActionColorsRequireColorized" />
<java-symbol type="dimen" name="preference_fragment_padding_bottom" />
<java-symbol type="dimen" name="preference_fragment_padding_side" />
<java-symbol type="drawable" name="expander_ic_maximized" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index e7e049da..16d720b 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -238,6 +238,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -271,6 +273,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -306,6 +310,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -340,6 +346,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -417,6 +425,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -449,6 +459,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -482,6 +494,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -531,6 +545,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -565,6 +581,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -597,6 +615,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -631,6 +651,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -664,6 +686,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -697,6 +721,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -730,6 +756,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -763,6 +791,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -800,6 +830,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -834,6 +866,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -865,6 +899,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -1069,6 +1105,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1101,6 +1139,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1134,6 +1174,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1169,6 +1211,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1203,6 +1247,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1281,6 +1327,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1316,6 +1364,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1352,6 +1402,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1426,6 +1478,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1463,6 +1517,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1498,6 +1554,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1532,6 +1590,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1565,6 +1625,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1598,6 +1660,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1629,6 +1693,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1750,6 +1816,8 @@
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
@@ -1782,6 +1850,8 @@
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1824,6 +1894,8 @@
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
@@ -1859,6 +1931,8 @@
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp
index e178776..2789e9f 100644
--- a/core/tests/GameManagerTests/Android.bp
+++ b/core/tests/GameManagerTests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksCoreGameManagerTests",
// Include all test java files
diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp
index ade97b8..1c0ea83 100644
--- a/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp
+++ b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BatteryStatsLoadTests",
srcs: ["src/**/*.java"],
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/Android.bp b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp
index 1e0498b..c2e7d81 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/Android.bp
+++ b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BatteryStatsViewer",
srcs: ["src/**/*.java"],
diff --git a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java
index 266ff3d..1c6b3cc 100644
--- a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java
@@ -33,17 +33,20 @@
@Test
public void testEquals() {
- ExternalTimeSuggestion one = new ExternalTimeSuggestion(ARBITRARY_TIME);
+ ExternalTimeSuggestion one = new ExternalTimeSuggestion(
+ ARBITRARY_TIME.getReferenceTimeMillis(),
+ ARBITRARY_TIME.getValue());
assertEquals(one, one);
- ExternalTimeSuggestion two = new ExternalTimeSuggestion(ARBITRARY_TIME);
+ ExternalTimeSuggestion two = new ExternalTimeSuggestion(
+ ARBITRARY_TIME.getReferenceTimeMillis(),
+ ARBITRARY_TIME.getValue());
assertEquals(one, two);
assertEquals(two, one);
- TimestampedValue<Long> differentTime = new TimestampedValue<>(
+ ExternalTimeSuggestion three = new ExternalTimeSuggestion(
ARBITRARY_TIME.getReferenceTimeMillis() + 1,
ARBITRARY_TIME.getValue());
- ExternalTimeSuggestion three = new ExternalTimeSuggestion(differentTime);
assertNotEquals(one, three);
assertNotEquals(three, one);
@@ -55,7 +58,9 @@
@Test
public void testParcelable() {
- ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion(ARBITRARY_TIME);
+ ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion(
+ ARBITRARY_TIME.getReferenceTimeMillis(),
+ ARBITRARY_TIME.getValue());
assertRoundTripParcelable(suggestion);
// DebugInfo should also be stored (but is not checked by equals())
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
new file mode 100644
index 0000000..412b367
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -0,0 +1,227 @@
+/*
+ * 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.hardware.input;
+
+import static android.hardware.lights.LightsRequest.Builder;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+import android.view.InputDevice;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link InputDeviceLightsManager}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:InputDeviceLightsManagerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class InputDeviceLightsManagerTest {
+ private static final String TAG = "InputDeviceLightsManagerTest";
+
+ private static final int DEVICE_ID = 1000;
+ private static final int PLAYER_ID = 3;
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private InputManager mInputManager;
+
+ @Mock private IInputManager mIInputManagerMock;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+
+ when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+ createInputDevice(DEVICE_ID));
+
+ mInputManager = InputManager.resetInstance(mIInputManagerMock);
+
+ ArrayMap<Integer, LightState> lightStatesById = new ArrayMap<>();
+ doAnswer(invocation -> {
+ final int[] lightIds = (int[]) invocation.getArguments()[1];
+ final LightState[] lightStates =
+ (LightState[]) invocation.getArguments()[2];
+ for (int i = 0; i < lightIds.length; i++) {
+ lightStatesById.put(lightIds[i], lightStates[i]);
+ }
+ return null;
+ }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID),
+ any(int[].class), any(LightState[].class), any(IBinder.class));
+
+ doAnswer(invocation -> {
+ int lightId = (int) invocation.getArguments()[1];
+ if (lightStatesById.containsKey(lightId)) {
+ return lightStatesById.get(lightId);
+ }
+ return new LightState(0);
+ }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt());
+ }
+
+ @After
+ public void tearDown() {
+ InputManager.clearInstance();
+ }
+
+ private InputDevice createInputDevice(int id) {
+ return new InputDevice(id, 0 /* generation */, 0 /* controllerNumber */, "name",
+ 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
+ 0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */,
+ false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
+ false /* hasSensor */, false /* hasBattery */);
+ }
+
+ private void mockLights(Light[] lights) throws Exception {
+ // Mock the Lights returned form InputManagerService
+ when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn(
+ new ArrayList(Arrays.asList(lights)));
+ }
+
+ @Test
+ public void testGetInputDeviceLights() throws Exception {
+ InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
+ assertNotNull(device);
+
+ Light[] mockedLights = {
+ new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_SINGLE),
+ new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_PLAYER_ID)
+ };
+ mockLights(mockedLights);
+
+ LightsManager lightsManager = device.getLightsManager();
+ List<Light> lights = lightsManager.getLights();
+ verify(mIInputManagerMock).getLights(eq(DEVICE_ID));
+ assertEquals(lights, Arrays.asList(mockedLights));
+ }
+
+ @Test
+ public void testControlMultipleLights() throws Exception {
+ InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
+ assertNotNull(device);
+
+ Light[] mockedLights = {
+ new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ new Light(4 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB)
+ };
+ mockLights(mockedLights);
+
+ LightsManager lightsManager = device.getLightsManager();
+ List<Light> lightList = lightsManager.getLights();
+ LightState[] states = new LightState[]{new LightState(0xf1), new LightState(0xf2),
+ new LightState(0xf3)};
+ // Open a session to request turn 3/4 lights on:
+ LightsManager.LightsSession session = lightsManager.openSession();
+ session.requestLights(new Builder()
+ .addLight(lightsManager.getLights().get(0), states[0])
+ .addLight(lightsManager.getLights().get(1), states[1])
+ .addLight(lightsManager.getLights().get(2), states[2])
+ .build());
+ IBinder token = session.getToken();
+
+ verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+ any(String.class), eq(token));
+ verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
+ eq(states), eq(token));
+
+ // Then all 3 should turn on.
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor())
+ .isEqualTo(0xf1);
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(1)).getColor())
+ .isEqualTo(0xf2);
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(2)).getColor())
+ .isEqualTo(0xf3);
+
+ // And the 4th should remain off.
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(3)).getColor())
+ .isEqualTo(0x00);
+
+ // close session
+ session.close();
+ verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+ }
+
+ @Test
+ public void testControlPlayerIdLight() throws Exception {
+ InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
+ assertNotNull(device);
+
+ Light[] mockedLights = {
+ new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_PLAYER_ID),
+ new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_SINGLE),
+ new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ };
+ mockLights(mockedLights);
+
+ LightsManager lightsManager = device.getLightsManager();
+ List<Light> lightList = lightsManager.getLights();
+ LightState[] states = new LightState[]{new LightState(0xf1, PLAYER_ID)};
+ // Open a session to request set Player ID light:
+ LightsManager.LightsSession session = lightsManager.openSession();
+ session.requestLights(new Builder()
+ .addLight(lightsManager.getLights().get(0), states[0])
+ .build());
+ IBinder token = session.getToken();
+
+ verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+ any(String.class), eq(token));
+ verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
+ eq(states), eq(token));
+
+ // Verify the light state
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor())
+ .isEqualTo(0xf1);
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getPlayerId())
+ .isEqualTo(PLAYER_ID);
+
+ // close session
+ session.close();
+ verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+ }
+}
diff --git a/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java b/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java
new file mode 100644
index 0000000..8906149
--- /dev/null
+++ b/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.inputmethodservice;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ServiceTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeoutException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodServiceTest {
+ private InputMethodService mService;
+ private Context mContext;
+ @Rule
+ public final ServiceTestRule serviceRule = new ServiceTestRule();
+
+ @Before
+ public void setUp() throws TimeoutException {
+ mContext = getInstrumentation().getContext();
+ mService = new InputMethodService();
+ }
+
+ @Test
+ public void testShouldImeRestartForConfig() throws Exception {
+ // Make sure we preserve Pre-S behavior i.e. Service restarts.
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R;
+ Configuration config = mContext.getResources().getConfiguration();
+ mService.setLastKnownConfig(config);
+ assertTrue("IME should restart for Pre-S",
+ mService.shouldImeRestartForConfig(config));
+
+ // IME shouldn't restart on targetSdk S+ (with no config changes).
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.S;
+ assertFalse("IME shouldn't restart for S+",
+ mService.shouldImeRestartForConfig(config));
+
+ // Screen density changed but IME doesn't handle congfigChanges
+ config.densityDpi = 99;
+ assertTrue("IME should restart for unhandled configChanges",
+ mService.shouldImeRestartForConfig(config));
+
+ // opt-in IME to handle config changes.
+ mService.setHandledConfigChanges(ActivityInfo.CONFIG_DENSITY);
+ assertFalse("IME shouldn't restart for S+ since it handles configChanges",
+ mService.shouldImeRestartForConfig(config));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index 5031ff9..80165f0 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -1,7 +1,7 @@
# Input
-per-file *MotionEventTest.* = michaelwr@google.com, svv@google.com
-per-file *KeyEventTest.* = michaelwr@google.com, svv@google.com
-per-file VelocityTest.java = michaelwr@google.com, svv@google.com
+per-file *MotionEventTest.* = file:/services/core/java/com/android/server/input/OWNERS
+per-file *KeyEventTest.* = file:/services/core/java/com/android/server/input/OWNERS
+per-file VelocityTest.java = file:/services/core/java/com/android/server/input/OWNERS
# WindowManager
per-file *Display* = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 10aaf31..9cb7876 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -105,7 +105,8 @@
mTracker = Mockito.spy(
new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
mSurfaceControlWrapper, mChoreographer, mWrapper,
- /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1));
+ /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1,
+ null));
doNothing().when(mTracker).triggerPerfetto();
}
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index c4c475b..8f4948c 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -96,7 +96,7 @@
new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
mock(FrameTracker.ChoreographerWrapper.class),
new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
- /*traceThresholdFrameTimeMillis=*/ -1));
+ /*traceThresholdFrameTimeMillis=*/ -1, null));
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
// Simulate a trace session and see if begin / end are invoked.
@@ -123,21 +123,12 @@
@Test
public void testCheckInitState() {
InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
+ View view = new View(mActivity);
+ assertThat(view.isAttachedToWindow()).isFalse();
- // Should return false if invoking begin / end without init invocation.
- assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+ // Should return false if the view passed in is not attached to window yet.
+ assertThat(monitor.begin(view, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
-
- // Everything should be fine if invoking init first.
- boolean thrown = false;
- try {
- assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
- assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
- } catch (Exception ex) {
- thrown = true;
- } finally {
- assertThat(thrown).isFalse();
- }
}
@Test
@@ -152,7 +143,7 @@
new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
mock(FrameTracker.ChoreographerWrapper.class),
new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
- /*traceThresholdFrameTimeMillis=*/ -1));
+ /*traceThresholdFrameTimeMillis=*/ -1, null));
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
assertThat(monitor.begin(mView, session.getCuj())).isTrue();
diff --git a/core/tests/devicestatetests/Android.bp b/core/tests/devicestatetests/Android.bp
index 409b77b..f7b5932 100644
--- a/core/tests/devicestatetests/Android.bp
+++ b/core/tests/devicestatetests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksCoreDeviceStateManagerTests",
// Include all test java files
diff --git a/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp b/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp
index 42421ce..3536c40 100644
--- a/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp
@@ -50,4 +50,5 @@
key: "com.android.overlaytest.overlaid.key",
apps: ["OverlayRemountedTest_Target"],
installable: false,
+ updatable: false,
}
diff --git a/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp b/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp
index 0b52dcc..f041404 100644
--- a/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp
@@ -50,4 +50,5 @@
key: "com.android.overlaytest.overlay.key",
apps: ["OverlayRemountedTest_Overlay"],
installable: false,
+ updatable: false,
}
diff --git a/data/etc/car/com.android.car.bugreport.xml b/data/etc/car/com.android.car.bugreport.xml
index 432a838..c3642d88 100644
--- a/data/etc/car/com.android.car.bugreport.xml
+++ b/data/etc/car/com.android.car.bugreport.xml
@@ -20,5 +20,6 @@
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.READ_LOGS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
index 489ce1b..cdeb8e4 100644
--- a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
+++ b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
@@ -16,12 +16,21 @@
-->
<permissions>
<privapp-permissions package="com.google.android.car.networking.preferenceupdater">
- <permission name="android.permission.ACCESS_NETWORK_STATE"/>
+ <permission name="android.permission.ACCESS_NETWORK_STATE" />
<permission name="android.permission.ACCESS_WIFI_STATE"/>
<permission name="android.permission.ACTIVITY_EMBEDDING"/>
+ <permission name="android.permission.CHANGE_NETWORK_STATE" />
+ <permission name="android.permission.CONNECTIVITY_INTERNAL" />
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+ <permission name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERNCE" />
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
- <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+ <permission name="android.permission.INTERNET" />
<permission name="android.permission.LOCATION_HARDWARE"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS" />
+ <permission name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <permission name="android.permission.WAKE_LOCK" />
+ <permission name="android.permission.WRITE_SETTINGS" />
+ <permission name="android.car.permission.CAR_DRIVING_STATE" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 5633de3..cabfad4 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1321,6 +1321,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
},
+ "-567946587": {
+ "message": "Requested redraw for orientation change: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-561092364": {
"message": "onPointerDownOutsideFocusLocked called on %s",
"level": "INFO",
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 2db4c5d..4f188cc 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -223,7 +223,7 @@
<alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" />
<family name="serif">
- <font weight="400" style="normal">NotoSerif-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSerif.ttf</font>
<font weight="700" style="normal">NotoSerif-Bold.ttf</font>
<font weight="400" style="italic">NotoSerif-Italic.ttf</font>
<font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
@@ -275,144 +275,144 @@
<!-- fallback fonts -->
<family lang="und-Arab" variant="elegant">
- <font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoNaskhArabic.ttf</font>
<font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
</family>
<family lang="und-Arab" variant="compact">
- <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
+ <font weight="400" style="normal">NotoNaskhArabicUI.ttf</font>
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
<family lang="und-Ethi">
- <font weight="400" style="normal">NotoSansEthiopic-VF.ttf
+ <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansEthiopic-VF.ttf
+ <font weight="500" style="normal">NotoSansEthiopic-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansEthiopic-VF.ttf
+ <font weight="600" style="normal">NotoSansEthiopic-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansEthiopic-VF.ttf
+ <font weight="700" style="normal">NotoSansEthiopic-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Hebr">
- <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansHebrew.ttf</font>
<font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
<font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
</family>
<family lang="und-Thai" variant="elegant">
- <font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansThai.ttf</font>
<font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai-Regular.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai.ttf</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
</family>
<family lang="und-Thai" variant="compact">
- <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansThaiUI.ttf</font>
<font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
</family>
<family lang="und-Armn">
- <font weight="400" style="normal">NotoSansArmenian-VF.ttf
+ <font weight="400" style="normal">NotoSansArmenian-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansArmenian-VF.ttf
+ <font weight="500" style="normal">NotoSansArmenian-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansArmenian-VF.ttf
+ <font weight="600" style="normal">NotoSansArmenian-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansArmenian-VF.ttf
+ <font weight="700" style="normal">NotoSansArmenian-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Geor,und-Geok">
- <font weight="400" style="normal">NotoSansGeorgian-VF.ttf
+ <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansGeorgian-VF.ttf
+ <font weight="500" style="normal">NotoSansGeorgian-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansGeorgian-VF.ttf
+ <font weight="600" style="normal">NotoSansGeorgian-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansGeorgian-VF.ttf
+ <font weight="700" style="normal">NotoSansGeorgian-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Deva" variant="elegant">
- <font weight="400" style="normal">NotoSansDevanagari-VF.ttf
+ <font weight="400" style="normal">NotoSansDevanagari-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansDevanagari-VF.ttf
+ <font weight="500" style="normal">NotoSansDevanagari-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansDevanagari-VF.ttf
+ <font weight="600" style="normal">NotoSansDevanagari-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansDevanagari-VF.ttf
+ <font weight="700" style="normal">NotoSansDevanagari-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Deva" variant="compact">
- <font weight="400" style="normal">NotoSansDevanagariUI-VF.ttf
+ <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansDevanagariUI-VF.ttf
+ <font weight="500" style="normal">NotoSansDevanagariUI-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansDevanagariUI-VF.ttf
+ <font weight="600" style="normal">NotoSansDevanagariUI-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansDevanagariUI-VF.ttf
+ <font weight="700" style="normal">NotoSansDevanagariUI-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
@@ -421,347 +421,347 @@
danda characters.
-->
<family lang="und-Gujr" variant="elegant">
- <font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansGujarati.ttf</font>
<font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Gujr" variant="compact">
- <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansGujaratiUI.ttf</font>
<font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
</family>
<family lang="und-Guru" variant="elegant">
- <font weight="400" style="normal">NotoSansGurmukhi-VF.ttf
+ <font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansGurmukhi-VF.ttf
+ <font weight="500" style="normal">NotoSansGurmukhi-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansGurmukhi-VF.ttf
+ <font weight="600" style="normal">NotoSansGurmukhi-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansGurmukhi-VF.ttf
+ <font weight="700" style="normal">NotoSansGurmukhi-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Guru" variant="compact">
- <font weight="400" style="normal">NotoSansGurmukhiUI-VF.ttf
+ <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansGurmukhiUI-VF.ttf
+ <font weight="500" style="normal">NotoSansGurmukhiUI-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansGurmukhiUI-VF.ttf
+ <font weight="600" style="normal">NotoSansGurmukhiUI-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansGurmukhiUI-VF.ttf
+ <font weight="700" style="normal">NotoSansGurmukhiUI-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Taml" variant="elegant">
- <font weight="400" style="normal">NotoSansTamil-VF.ttf
+ <font weight="400" style="normal">NotoSansTamil-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTamil-VF.ttf
+ <font weight="500" style="normal">NotoSansTamil-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTamil-VF.ttf
+ <font weight="600" style="normal">NotoSansTamil-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTamil-VF.ttf
+ <font weight="700" style="normal">NotoSansTamil-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Taml" variant="compact">
- <font weight="400" style="normal">NotoSansTamilUI-VF.ttf
+ <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTamilUI-VF.ttf
+ <font weight="500" style="normal">NotoSansTamilUI-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTamilUI-VF.ttf
+ <font weight="600" style="normal">NotoSansTamilUI-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTamilUI-VF.ttf
+ <font weight="700" style="normal">NotoSansTamilUI-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Mlym" variant="elegant">
- <font weight="400" style="normal">NotoSansMalayalam-VF.ttf
+ <font weight="400" style="normal">NotoSansMalayalam-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansMalayalam-VF.ttf
+ <font weight="500" style="normal">NotoSansMalayalam-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansMalayalam-VF.ttf
+ <font weight="600" style="normal">NotoSansMalayalam-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansMalayalam-VF.ttf
+ <font weight="700" style="normal">NotoSansMalayalam-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Mlym" variant="compact">
- <font weight="400" style="normal">NotoSansMalayalamUI-VF.ttf
+ <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansMalayalamUI-VF.ttf
+ <font weight="500" style="normal">NotoSansMalayalamUI-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansMalayalamUI-VF.ttf
+ <font weight="600" style="normal">NotoSansMalayalamUI-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansMalayalamUI-VF.ttf
+ <font weight="700" style="normal">NotoSansMalayalamUI-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Beng" variant="elegant">
- <font weight="400" style="normal">NotoSansBengali-VF.ttf
+ <font weight="400" style="normal">NotoSansBengali-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansBengali-VF.ttf
+ <font weight="500" style="normal">NotoSansBengali-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansBengali-VF.ttf
+ <font weight="600" style="normal">NotoSansBengali-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansBengali-VF.ttf
+ <font weight="700" style="normal">NotoSansBengali-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Beng" variant="compact">
- <font weight="400" style="normal">NotoSansBengaliUI-VF.ttf
+ <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansBengaliUI-VF.ttf
+ <font weight="500" style="normal">NotoSansBengaliUI-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansBengaliUI-VF.ttf
+ <font weight="600" style="normal">NotoSansBengaliUI-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansBengaliUI-VF.ttf
+ <font weight="700" style="normal">NotoSansBengaliUI-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Telu" variant="elegant">
- <font weight="400" style="normal">NotoSansTelugu-VF.ttf
+ <font weight="400" style="normal">NotoSansTelugu-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTelugu-VF.ttf
+ <font weight="500" style="normal">NotoSansTelugu-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTelugu-VF.ttf
+ <font weight="600" style="normal">NotoSansTelugu-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTelugu-VF.ttf
+ <font weight="700" style="normal">NotoSansTelugu-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Telu" variant="compact">
- <font weight="400" style="normal">NotoSansTeluguUI-VF.ttf
+ <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTeluguUI-VF.ttf
+ <font weight="500" style="normal">NotoSansTeluguUI-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTeluguUI-VF.ttf
+ <font weight="600" style="normal">NotoSansTeluguUI-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTeluguUI-VF.ttf
+ <font weight="700" style="normal">NotoSansTeluguUI-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Knda" variant="elegant">
- <font weight="400" style="normal">NotoSansKannada-VF.ttf
+ <font weight="400" style="normal">NotoSansKannada-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansKannada-VF.ttf
+ <font weight="500" style="normal">NotoSansKannada-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansKannada-VF.ttf
+ <font weight="600" style="normal">NotoSansKannada-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansKannada-VF.ttf
+ <font weight="700" style="normal">NotoSansKannada-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Knda" variant="compact">
- <font weight="400" style="normal">NotoSansKannadaUI-VF.ttf
+ <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansKannadaUI-VF.ttf
+ <font weight="500" style="normal">NotoSansKannadaUI-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansKannadaUI-VF.ttf
+ <font weight="600" style="normal">NotoSansKannadaUI-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansKannadaUI-VF.ttf
+ <font weight="700" style="normal">NotoSansKannadaUI-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Orya" variant="elegant">
- <font weight="400" style="normal">NotoSansOriya-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansOriya.ttf</font>
<font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
</family>
<family lang="und-Orya" variant="compact">
- <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansOriyaUI.ttf</font>
<font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
</family>
<family lang="und-Sinh" variant="elegant">
- <font weight="400" style="normal">NotoSansSinhala-VF.ttf
+ <font weight="400" style="normal">NotoSansSinhala-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansSinhala-VF.ttf
+ <font weight="500" style="normal">NotoSansSinhala-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansSinhala-VF.ttf
+ <font weight="600" style="normal">NotoSansSinhala-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansSinhala-VF.ttf
+ <font weight="700" style="normal">NotoSansSinhala-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Sinh" variant="compact">
- <font weight="400" style="normal">NotoSansSinhalaUI-VF.ttf
+ <font weight="400" style="normal">NotoSansSinhalaUI-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansSinhalaUI-VF.ttf
+ <font weight="500" style="normal">NotoSansSinhalaUI-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansSinhalaUI-VF.ttf
+ <font weight="600" style="normal">NotoSansSinhalaUI-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansSinhalaUI-VF.ttf
+ <font weight="700" style="normal">NotoSansSinhalaUI-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Khmr" variant="elegant">
- <font weight="100" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="100" style="normal">NotoSansKhmer-Regular.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="26.0"/>
</font>
- <font weight="200" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="200" style="normal">NotoSansKhmer-Regular.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="39.0"/>
</font>
- <font weight="300" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="300" style="normal">NotoSansKhmer-Regular.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="58.0"/>
</font>
- <font weight="400" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="400" style="normal">NotoSansKhmer-Regular.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="90.0"/>
</font>
- <font weight="500" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="500" style="normal">NotoSansKhmer-Regular.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="108.0"/>
</font>
- <font weight="600" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="600" style="normal">NotoSansKhmer-Regular.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="128.0"/>
</font>
- <font weight="700" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="700" style="normal">NotoSansKhmer-Regular.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="151.0"/>
</font>
- <font weight="800" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="800" style="normal">NotoSansKhmer-Regular.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="169.0"/>
</font>
- <font weight="900" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="900" style="normal">NotoSansKhmer-Regular.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="190.0"/>
</font>
@@ -769,17 +769,17 @@
<font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
</family>
<family lang="und-Khmr" variant="compact">
- <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansKhmerUI.ttf</font>
<font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="elegant">
- <font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansLao.ttf</font>
<font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao-Regular.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao.ttf</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="compact">
- <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansLaoUI.ttf</font>
<font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
</family>
<family lang="und-Mymr" variant="elegant">
@@ -795,56 +795,56 @@
<font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
</family>
<family lang="und-Thaa">
- <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansThaana.ttf</font>
<font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
</family>
<family lang="und-Cham">
- <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansCham.ttf</font>
<font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
</family>
<family lang="und-Ahom">
<font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
</family>
<family lang="und-Adlm">
- <font weight="400" style="normal">NotoSansAdlam-VF.ttf
+ <font weight="400" style="normal">NotoSansAdlam-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansAdlam-VF.ttf
+ <font weight="500" style="normal">NotoSansAdlam-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansAdlam-VF.ttf
+ <font weight="600" style="normal">NotoSansAdlam-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansAdlam-VF.ttf
+ <font weight="700" style="normal">NotoSansAdlam-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Avst">
- <font weight="400" style="normal">NotoSansAvestan-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansAvestan.ttf</font>
</family>
<family lang="und-Bali">
- <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansBalinese.ttf</font>
</family>
<family lang="und-Bamu">
- <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansBamum.ttf</font>
</family>
<family lang="und-Batk">
- <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansBatak.ttf</font>
</family>
<family lang="und-Brah">
- <font weight="400" style="normal">NotoSansBrahmi-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansBrahmi.ttf</font>
</family>
<family lang="und-Bugi">
- <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansBuginese.ttf</font>
</family>
<family lang="und-Buhd">
- <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansBuhid.ttf</font>
</family>
<family lang="und-Cans">
- <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansCanadianAboriginal.ttf</font>
</family>
<family lang="und-Cari">
- <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansCarian.ttf</font>
</family>
<family lang="und-Cakm">
<font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
@@ -853,164 +853,164 @@
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
</family>
<family lang="und-Copt">
- <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansCoptic.ttf</font>
</family>
<family lang="und-Xsux">
- <font weight="400" style="normal">NotoSansCuneiform-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansCuneiform.ttf</font>
</family>
<family lang="und-Cprt">
- <font weight="400" style="normal">NotoSansCypriot-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansCypriot.ttf</font>
</family>
<family lang="und-Dsrt">
- <font weight="400" style="normal">NotoSansDeseret-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansDeseret.ttf</font>
</family>
<family lang="und-Egyp">
- <font weight="400" style="normal">NotoSansEgyptianHieroglyphs-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansEgyptianHieroglyphs.ttf</font>
</family>
<family lang="und-Elba">
<font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
</family>
<family lang="und-Glag">
- <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansGlagolitic.ttf</font>
</family>
<family lang="und-Goth">
- <font weight="400" style="normal">NotoSansGothic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansGothic.ttf</font>
</family>
<family lang="und-Hano">
- <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansHanunoo.ttf</font>
</family>
<family lang="und-Armi">
- <font weight="400" style="normal">NotoSansImperialAramaic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansImperialAramaic.ttf</font>
</family>
<family lang="und-Phli">
- <font weight="400" style="normal">NotoSansInscriptionalPahlavi-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansInscriptionalPahlavi.ttf</font>
</family>
<family lang="und-Prti">
- <font weight="400" style="normal">NotoSansInscriptionalParthian-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansInscriptionalParthian.ttf</font>
</family>
<family lang="und-Java">
<font weight="400" style="normal">NotoSansJavanese-Regular.otf</font>
</family>
<family lang="und-Kthi">
- <font weight="400" style="normal">NotoSansKaithi-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansKaithi.ttf</font>
</family>
<family lang="und-Kali">
- <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansKayahLi.ttf</font>
</family>
<family lang="und-Khar">
- <font weight="400" style="normal">NotoSansKharoshthi-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansKharoshthi.ttf</font>
</family>
<family lang="und-Lepc">
- <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansLepcha.ttf</font>
</family>
<family lang="und-Limb">
- <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansLimbu.ttf</font>
</family>
<family lang="und-Linb">
- <font weight="400" style="normal">NotoSansLinearB-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansLinearB.ttf</font>
</family>
<family lang="und-Lisu">
- <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansLisu.ttf</font>
</family>
<family lang="und-Lyci">
- <font weight="400" style="normal">NotoSansLycian-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansLycian.ttf</font>
</family>
<family lang="und-Lydi">
- <font weight="400" style="normal">NotoSansLydian-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansLydian.ttf</font>
</family>
<family lang="und-Mand">
- <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansMandaic.ttf</font>
</family>
<family lang="und-Mtei">
- <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansMeeteiMayek.ttf</font>
</family>
<family lang="und-Talu">
- <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansNewTaiLue.ttf</font>
</family>
<family lang="und-Nkoo">
- <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansNKo.ttf</font>
</family>
<family lang="und-Ogam">
- <font weight="400" style="normal">NotoSansOgham-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansOgham.ttf</font>
</family>
<family lang="und-Olck">
- <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansOlChiki.ttf</font>
</family>
<family lang="und-Ital">
- <font weight="400" style="normal">NotoSansOldItalic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansOldItalic.ttf</font>
</family>
<family lang="und-Xpeo">
- <font weight="400" style="normal">NotoSansOldPersian-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansOldPersian.ttf</font>
</family>
<family lang="und-Sarb">
- <font weight="400" style="normal">NotoSansOldSouthArabian-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansOldSouthArabian.ttf</font>
</family>
<family lang="und-Orkh">
- <font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansOldTurkic.ttf</font>
</family>
<family lang="und-Osge">
<font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
</family>
<family lang="und-Osma">
- <font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansOsmanya.ttf</font>
</family>
<family lang="und-Phnx">
- <font weight="400" style="normal">NotoSansPhoenician-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansPhoenician.ttf</font>
</family>
<family lang="und-Rjng">
- <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansRejang.ttf</font>
</family>
<family lang="und-Runr">
- <font weight="400" style="normal">NotoSansRunic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansRunic.ttf</font>
</family>
<family lang="und-Samr">
- <font weight="400" style="normal">NotoSansSamaritan-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansSamaritan.ttf</font>
</family>
<family lang="und-Saur">
- <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansSaurashtra.ttf</font>
</family>
<family lang="und-Shaw">
- <font weight="400" style="normal">NotoSansShavian-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansShavian.ttf</font>
</family>
<family lang="und-Sund">
- <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansSundanese.ttf</font>
</family>
<family lang="und-Sylo">
- <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansSylotiNagri.ttf</font>
</family>
<!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. -->
<family lang="und-Syre">
- <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansSyriacEstrangela.ttf</font>
</family>
<family lang="und-Syrn">
- <font weight="400" style="normal">NotoSansSyriacEastern-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansSyriacEastern.ttf</font>
</family>
<family lang="und-Syrj">
- <font weight="400" style="normal">NotoSansSyriacWestern-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansSyriacWestern.ttf</font>
</family>
<family lang="und-Tglg">
- <font weight="400" style="normal">NotoSansTagalog-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansTagalog.ttf</font>
</family>
<family lang="und-Tagb">
- <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansTagbanwa.ttf</font>
</family>
<family lang="und-Lana">
- <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansTaiTham.ttf</font>
</family>
<family lang="und-Tavt">
- <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansTaiViet.ttf</font>
</family>
<family lang="und-Tibt">
- <font weight="400" style="normal">NotoSerifTibetan-VF.ttf
+ <font weight="400" style="normal">NotoSerifTibetan-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSerifTibetan-VF.ttf
+ <font weight="500" style="normal">NotoSerifTibetan-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSerifTibetan-VF.ttf
+ <font weight="600" style="normal">NotoSerifTibetan-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSerifTibetan-VF.ttf
+ <font weight="700" style="normal">NotoSerifTibetan-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
@@ -1018,29 +1018,29 @@
<font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font>
</family>
<family lang="und-Ugar">
- <font weight="400" style="normal">NotoSansUgaritic-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansUgaritic.ttf</font>
</family>
<family lang="und-Vaii">
- <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansVai.ttf</font>
</family>
<family>
<font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
</family>
<family lang="zh-Hans">
- <font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
- <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="2">NotoSansCJKjp-Regular.otc</font>
+ <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
</family>
<family lang="zh-Hant,zh-Bopo">
- <font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
- <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="3">NotoSansCJKjp-Regular.otc</font>
+ <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
</family>
<family lang="ja">
- <font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font>
- <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="0">NotoSansCJKjp-Regular.otc</font>
+ <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
</family>
<family lang="ko">
- <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
- <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="1">NotoSansCJKjp-Regular.otc</font>
+ <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
</family>
<family lang="und-Zsye">
<font weight="400" style="normal">NotoColorEmoji.ttf</font>
@@ -1053,16 +1053,16 @@
override the East Asian punctuation for Chinese.
-->
<family lang="und-Tale">
- <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansTaiLe.ttf</font>
</family>
<family lang="und-Yiii">
- <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansYi.ttf</font>
</family>
<family lang="und-Mong">
- <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansMongolian.ttf</font>
</family>
<family lang="und-Phag">
- <font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansPhagsPa.ttf</font>
</family>
<family lang="und-Hluw">
<font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
@@ -1152,72 +1152,72 @@
<font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font>
</family>
<family lang="und-Medf">
- <font weight="400" style="normal">NotoSansMedefaidrin-VF.ttf
+ <font weight="400" style="normal">NotoSansMedefaidrin-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansMedefaidrin-VF.ttf
+ <font weight="500" style="normal">NotoSansMedefaidrin-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansMedefaidrin-VF.ttf
+ <font weight="600" style="normal">NotoSansMedefaidrin-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansMedefaidrin-VF.ttf
+ <font weight="700" style="normal">NotoSansMedefaidrin-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Soyo">
- <font weight="400" style="normal">NotoSansSoyombo-VF.ttf
+ <font weight="400" style="normal">NotoSansSoyombo-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansSoyombo-VF.ttf
+ <font weight="500" style="normal">NotoSansSoyombo-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansSoyombo-VF.ttf
+ <font weight="600" style="normal">NotoSansSoyombo-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansSoyombo-VF.ttf
+ <font weight="700" style="normal">NotoSansSoyombo-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Takr">
- <font weight="400" style="normal">NotoSansTakri-VF.ttf
+ <font weight="400" style="normal">NotoSansTakri-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTakri-VF.ttf
+ <font weight="500" style="normal">NotoSansTakri-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTakri-VF.ttf
+ <font weight="600" style="normal">NotoSansTakri-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTakri-VF.ttf
+ <font weight="700" style="normal">NotoSansTakri-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Hmnp">
- <font weight="400" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+ <font weight="400" style="normal">NotoSerifHmongNyiakeng-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+ <font weight="500" style="normal">NotoSerifHmongNyiakeng-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+ <font weight="600" style="normal">NotoSerifHmongNyiakeng-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+ <font weight="700" style="normal">NotoSerifHmongNyiakeng-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Yezi">
- <font weight="400" style="normal">NotoSerifYezidi-VF.ttf
+ <font weight="400" style="normal">NotoSerifYezidi-Regular.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSerifYezidi-VF.ttf
+ <font weight="500" style="normal">NotoSerifYezidi-Regular.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSerifYezidi-VF.ttf
+ <font weight="600" style="normal">NotoSerifYezidi-Regular.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSerifYezidi-VF.ttf
+ <font weight="700" style="normal">NotoSerifYezidi-Regular.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
diff --git a/data/keyboards/OWNERS b/data/keyboards/OWNERS
index c4f6df8..0ce8350 100644
--- a/data/keyboards/OWNERS
+++ b/data/keyboards/OWNERS
@@ -1,5 +1,3 @@
set noparent
-michaelwr@google.com
-svv@google.com
-lzye@google.com
+include /services/core/java/com/android/server/input/OWNERS
diff --git a/data/keyboards/Vendor_057e_Product_2009.kl b/data/keyboards/Vendor_057e_Product_2009.kl
index 3c6b11e..7491ee5 100644
--- a/data/keyboards/Vendor_057e_Product_2009.kl
+++ b/data/keyboards/Vendor_057e_Product_2009.kl
@@ -74,3 +74,11 @@
# Home key
key 0x13c HOME
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/graphics/java/android/graphics/BlurShader.java b/graphics/java/android/graphics/BlurShader.java
deleted file mode 100644
index 2e4bd7d..0000000
--- a/graphics/java/android/graphics/BlurShader.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * A subclass of shader that blurs input from another {@link android.graphics.Shader} instance
- * or all the drawing commands with the {@link android.graphics.Paint} that this shader is
- * attached to.
- */
-public final class BlurShader extends Shader {
-
- private final float mRadiusX;
- private final float mRadiusY;
- private final Shader mInputShader;
- private final TileMode mEdgeTreatment;
-
- private long mNativeInputShader = 0;
-
- /**
- * Create a {@link BlurShader} that blurs the contents of the optional input shader
- * with the specified radius along the x and y axis. If no input shader is provided
- * then all drawing commands issued with a {@link android.graphics.Paint} that this
- * shader is installed in will be blurred.
- *
- * This uses a default {@link TileMode#DECAL} for edge treatment
- *
- * @param radiusX Radius of blur along the X axis
- * @param radiusY Radius of blur along the Y axis
- * @param inputShader Input shader that provides the content to be blurred
- */
- public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) {
- this(radiusX, radiusY, inputShader, TileMode.DECAL);
- }
-
- /**
- * Create a {@link BlurShader} that blurs the contents of the optional input shader
- * with the specified radius along the x and y axis. If no input shader is provided
- * then all drawing commands issued with a {@link android.graphics.Paint} that this
- * shader is installed in will be blurred
- * @param radiusX Radius of blur along the X axis
- * @param radiusY Radius of blur along the Y axis
- * @param inputShader Input shader that provides the content to be blurred
- * @param edgeTreatment Policy for how to blur content near edges of the blur shader
- */
- public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader,
- @NonNull TileMode edgeTreatment) {
- mRadiusX = radiusX;
- mRadiusY = radiusY;
- mInputShader = inputShader;
- mEdgeTreatment = edgeTreatment;
- }
-
- /** @hide **/
- @Override
- protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
- mNativeInputShader = mInputShader != null
- ? mInputShader.getNativeInstance(filterFromPaint) : 0;
- return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader,
- mEdgeTreatment.nativeInt);
- }
-
- /** @hide **/
- @Override
- protected boolean shouldDiscardNativeInstance(boolean filterFromPaint) {
- long currentNativeInstance = mInputShader != null
- ? mInputShader.getNativeInstance(filterFromPaint) : 0;
- return mNativeInputShader != currentNativeInstance;
- }
-
- private static native long nativeCreate(long nativeMatrix, float radiusX, float radiusY,
- long inputShader, int edgeTreatment);
-}
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index d59abb5..189be53 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -69,28 +69,26 @@
// animation & drawing system
public static final int VSYNC = 3;
- // The time of the oldest input event
- public static final int OLDEST_INPUT_EVENT = 4;
-
- // The time of the newest input event
- public static final int NEWEST_INPUT_EVENT = 5;
+ // The id of the input event that caused the current frame
+ public static final int INPUT_EVENT_ID = 4;
// When input event handling started
- public static final int HANDLE_INPUT_START = 6;
+ public static final int HANDLE_INPUT_START = 5;
// When animation evaluations started
- public static final int ANIMATION_START = 7;
+ public static final int ANIMATION_START = 6;
// When ViewRootImpl#performTraversals() started
- public static final int PERFORM_TRAVERSALS_START = 8;
+ public static final int PERFORM_TRAVERSALS_START = 7;
// When View:draw() started
- public static final int DRAW_START = 9;
+ public static final int DRAW_START = 8;
// When the frame needs to be ready by
- public static final int FRAME_DEADLINE = 10;
+ public static final int FRAME_DEADLINE = 9;
// Must be the last one
+ // This value must be in sync with `UI_THREAD_FRAME_INFO_SIZE` in FrameInfo.h
private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1;
/** checkstyle */
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 4a92cf1..f6f770b 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -272,6 +272,16 @@
void positionChanged(long frameNumber, int left, int top, int right, int bottom);
/**
+ * Call to apply a stretch effect to any child SurfaceControl layers
+ *
+ * TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls?
+ *
+ * @hide
+ */
+ default void applyStretch(long frameNumber, float left, float top, float right,
+ float bottom, float vecX, float vecY, float maxStretch) { }
+
+ /**
* Called by native on RenderThread to notify that the view is no longer in the
* draw tree. UI thread is blocked at this point.
*
@@ -312,6 +322,14 @@
pul.positionLost(frameNumber);
}
}
+
+ @Override
+ public void applyStretch(long frameNumber, float left, float top, float right, float bottom,
+ float vecX, float vecY, float maxStretch) {
+ for (PositionUpdateListener pul : mListeners) {
+ pul.applyStretch(frameNumber, left, top, right, bottom, vecX, vecY, maxStretch);
+ }
+ }
}
/**
@@ -707,7 +725,7 @@
if (1.0 < vecY || vecY < -1.0) {
throw new IllegalArgumentException("vecY must be in the range [-1, 1], was " + vecY);
}
- if (top <= bottom || right <= left) {
+ if (top >= bottom || left >= right) {
throw new IllegalArgumentException(
"Stretch region must not be empty, got "
+ new RectF(left, top, right, bottom).toString());
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index f708298..d00f5f6 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -37,8 +37,6 @@
void setUserSelectable(String alias, boolean isUserSelectable);
int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
- int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
- out KeymasterCertificateChain chain);
boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
// APIs used by CertInstaller and DevicePolicyManager
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 97819c5..f0bcfe52 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -44,6 +44,8 @@
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
import android.util.Log;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -682,6 +684,33 @@
return null;
}
+ /**
+ * This prefix is used to disambiguate grant aliase strings from normal key alias strings.
+ * Technically, a key alias string can use the same prefix. However, a collision does not
+ * lead to privilege escalation, because grants are access controlled in the Keystore daemon.
+ * @hide
+ */
+ public static final String GRANT_ALIAS_PREFIX = "ks2_keychain_grant_id:";
+
+ private static KeyDescriptor getGrantDescriptor(String keyid) {
+ KeyDescriptor result = new KeyDescriptor();
+ result.domain = Domain.GRANT;
+ result.blob = null;
+ result.alias = null;
+ try {
+ result.nspace = Long.parseUnsignedLong(
+ keyid.substring(GRANT_ALIAS_PREFIX.length()), 16 /* radix */);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ return result;
+ }
+
+ /** @hide */
+ public static String getGrantString(KeyDescriptor key) {
+ return String.format(GRANT_ALIAS_PREFIX + "%016X", key.nspace);
+ }
+
/** @hide */
@Nullable @WorkerThread
public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias)
@@ -705,11 +734,23 @@
if (keyId == null) {
return null;
+ }
+
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ try {
+ return android.security.keystore2.AndroidKeyStoreProvider
+ .loadAndroidKeyStoreKeyPairFromKeystore(
+ KeyStore2.getInstance(),
+ getGrantDescriptor(keyId));
+ } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
+ throw new KeyChainException(e);
+ }
} else {
try {
return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
KeyStore.getInstance(), keyId, KeyStore.UID_SELF);
- } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
+ } catch (RuntimeException | UnrecoverableKeyException
+ | KeyPermanentlyInvalidatedException e) {
throw new KeyChainException(e);
}
}
@@ -827,11 +868,8 @@
@Deprecated
public static boolean isBoundKeyAlgorithm(
@NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
- if (!isKeyAlgorithmSupported(algorithm)) {
- return false;
- }
-
- return KeyStore.getInstance().isHardwareBacked(algorithm);
+ // All supported algorithms are hardware backed. Individual keys may not be.
+ return true;
}
/** @hide */
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index c20cf01..a6e3366 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -59,7 +59,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(mSpec.getKeystoreAlias());
out.writeInt(mSpec.getPurposes());
- out.writeInt(mSpec.getUid());
+ out.writeInt(mSpec.getNamespace());
out.writeInt(mSpec.getKeySize());
// Only needs to support RSAKeyGenParameterSpec and ECGenParameterSpec.
@@ -125,7 +125,7 @@
private ParcelableKeyGenParameterSpec(Parcel in) {
final String keystoreAlias = in.readString();
final int purposes = in.readInt();
- final int uid = in.readInt();
+ final int namespace = in.readInt();
final int keySize = in.readInt();
final int keySpecType = in.readInt();
@@ -177,7 +177,7 @@
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
mSpec = new KeyGenParameterSpec(
keystoreAlias,
- uid,
+ namespace,
keySize,
algorithmSpec,
certificateSubject,
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index b3bfd6a..e401add 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -154,7 +154,7 @@
private KeyGenParameterSpec mSpec;
private String mEntryAlias;
- private int mEntryUid;
+ private int mEntryNamespace;
private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
private int mKeymasterAlgorithm = -1;
private int mKeySizeBits;
@@ -218,7 +218,7 @@
}
mEntryAlias = spec.getKeystoreAlias();
- mEntryUid = spec.getUid();
+ mEntryNamespace = spec.getNamespace();
mSpec = spec;
mKeymasterAlgorithm = keymasterAlgorithm;
mKeySizeBits = spec.getKeySize();
@@ -439,7 +439,7 @@
private void resetAll() {
mEntryAlias = null;
- mEntryUid = KeyProperties.NAMESPACE_APPLICATION;
+ mEntryNamespace = KeyProperties.NAMESPACE_APPLICATION;
mJcaKeyAlgorithm = null;
mKeymasterAlgorithm = -1;
mKeymasterPurposes = null;
@@ -541,10 +541,10 @@
KeyDescriptor descriptor = new KeyDescriptor();
descriptor.alias = mEntryAlias;
- descriptor.domain = mEntryUid == KeyProperties.NAMESPACE_APPLICATION
+ descriptor.domain = mEntryNamespace == KeyProperties.NAMESPACE_APPLICATION
? Domain.APP
: Domain.SELINUX;
- descriptor.nspace = mEntryUid;
+ descriptor.nspace = mEntryNamespace;
descriptor.blob = null;
boolean success = false;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index e101115..35059ac 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -273,10 +273,10 @@
/** @hide **/
@NonNull
public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
- @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)
+ @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
AndroidKeyStoreKey key =
- loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace);
+ loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
if (key instanceof AndroidKeyStorePublicKey) {
AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key;
return new KeyPair(publicKey, publicKey.getPrivateKey());
@@ -336,7 +336,7 @@
@NonNull
public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
@NonNull KeyStore2 keyStore, @NonNull String alias, int namespace)
- throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
+ throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
KeyDescriptor descriptor = new KeyDescriptor();
if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
@@ -348,6 +348,18 @@
}
descriptor.alias = alias;
descriptor.blob = null;
+
+ final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
+ if (key instanceof AndroidKeyStorePublicKey) {
+ return ((AndroidKeyStorePublicKey) key).getPrivateKey();
+ } else {
+ return key;
+ }
+ }
+
+ private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
+ @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
+ throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
KeyEntryResponse response = null;
try {
response = keyStore.getKeyEntry(descriptor);
@@ -397,7 +409,7 @@
keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata,
new KeyStoreSecurityLevel(response.iSecurityLevel),
- keymasterAlgorithm).getPrivateKey();
+ keymasterAlgorithm);
} else {
throw new UnrecoverableKeyException("Key algorithm unknown");
}
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
index 347c2b4..0dea87c 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
@@ -14,35 +14,52 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.wm.shell.sizecompatui.SizeCompatHintPopup
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@android:color/background_light"
- android:orientation="vertical">
+ android:layout_height="wrap_content">
- <TextView
- android:layout_width="180dp"
- android:layout_height="wrap_content"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:paddingTop="10dp"
- android:text="@string/restart_button_description"
- android:textAlignment="viewStart"
- android:textColor="@android:color/primary_text_light"
- android:textSize="16sp" />
-
- <Button
- android:id="@+id/got_it"
+ <FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:includeFontPadding="false"
- android:layout_gravity="end"
- android:minHeight="36dp"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/got_it"
- android:textAllCaps="true"
- android:textColor="#3c78d8"
- android:textSize="16sp"
- android:textStyle="bold" />
+ android:gravity="center"
+ android:clipToPadding="false"
+ android:padding="@dimen/bubble_elevation">
-</LinearLayout>
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/background_light"
+ android:elevation="@dimen/bubble_elevation"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="180dp"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:paddingTop="10dp"
+ android:text="@string/restart_button_description"
+ android:textAlignment="viewStart"
+ android:textColor="@android:color/primary_text_light"
+ android:textSize="16sp"/>
+
+ <Button
+ android:id="@+id/got_it"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:includeFontPadding="false"
+ android:layout_gravity="end"
+ android:minHeight="36dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@string/got_it"
+ android:textAllCaps="true"
+ android:textColor="#3c78d8"
+ android:textSize="16sp"
+ android:textStyle="bold"/>
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+</com.android.wm.shell.sizecompatui.SizeCompatHintPopup>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 0ee1f06..8697be9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -124,6 +124,7 @@
private int mDesiredHeight;
@DimenRes
private int mDesiredHeightResId;
+ private int mTaskId;
/** for logging **/
@Nullable
@@ -162,7 +163,7 @@
*/
Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
- Executor mainExecutor) {
+ int taskId, Executor mainExecutor) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
mMetadataShortcutId = shortcutInfo.getId();
@@ -178,6 +179,7 @@
mTitle = title;
mShowBubbleUpdateDot = false;
mMainExecutor = mainExecutor;
+ mTaskId = taskId;
}
@VisibleForTesting(visibility = PRIVATE)
@@ -197,6 +199,7 @@
});
};
mMainExecutor = mainExecutor;
+ mTaskId = INVALID_TASK_ID;
setEntry(entry);
}
@@ -520,7 +523,7 @@
*/
@Override
public int getTaskId() {
- return mExpandedView != null ? mExpandedView.getTaskId() : INVALID_TASK_ID;
+ return mExpandedView != null ? mExpandedView.getTaskId() : mTaskId;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index 3108b02..2417552 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -28,7 +28,6 @@
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.common.annotations.ExternalThread
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -36,8 +35,11 @@
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
-internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps,
- private val mainExecutor : ShellExecutor) {
+internal class BubbleDataRepository(
+ context: Context,
+ private val launcherApps: LauncherApps,
+ private val mainExecutor: ShellExecutor
+) {
private val volatileRepository = BubbleVolatileRepository(launcherApps)
private val persistentRepository = BubblePersistentRepository(context)
@@ -78,7 +80,8 @@
b.key,
b.rawDesiredHeight,
b.rawDesiredHeightResId,
- b.title
+ b.title,
+ b.taskId
)
}
}
@@ -168,6 +171,7 @@
entity.desiredHeight,
entity.desiredHeightResId,
entity.title,
+ entity.taskId,
mainExecutor
) }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index a3edc20..a8ab406 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1539,19 +1539,16 @@
* Update bubble order and pointer position.
*/
public void updateBubbleOrder(List<Bubble> bubbles) {
- if (isExpansionAnimating()) {
- return;
- }
final Runnable reorder = () -> {
for (int i = 0; i < bubbles.size(); i++) {
Bubble bubble = bubbles.get(i);
mBubbleContainer.reorderView(bubble.getIconView(), i);
}
};
- if (mIsExpanded) {
+ if (mIsExpanded || isExpansionAnimating()) {
reorder.run();
updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
- } else {
+ } else if (!isExpansionAnimating()) {
List<View> bubbleViews = bubbles.stream()
.map(b -> b.getIconView()).collect(Collectors.toList());
mStackAnimationController.animateReorder(bubbleViews, reorder);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
index aeba302..d5cab5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
@@ -25,5 +25,6 @@
val key: String,
val desiredHeight: Int,
@DimenRes val desiredHeightResId: Int,
- val title: String? = null
+ val title: String? = null,
+ val taskId: Int
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
index fe72bd3..470011b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.bubbles.storage
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.util.Xml
import com.android.internal.util.FastXmlSerializer
import com.android.internal.util.XmlUtils
@@ -38,6 +39,7 @@
private const val ATTR_DESIRED_HEIGHT = "h"
private const val ATTR_DESIRED_HEIGHT_RES_ID = "hid"
private const val ATTR_TITLE = "t"
+private const val ATTR_TASK_ID = "tid"
/**
* Writes the bubbles in xml format into given output stream.
@@ -70,6 +72,7 @@
serializer.attribute(null, ATTR_DESIRED_HEIGHT, bubble.desiredHeight.toString())
serializer.attribute(null, ATTR_DESIRED_HEIGHT_RES_ID, bubble.desiredHeightResId.toString())
bubble.title?.let { serializer.attribute(null, ATTR_TITLE, it) }
+ serializer.attribute(null, ATTR_TASK_ID, bubble.taskId.toString())
serializer.endTag(null, TAG_BUBBLE)
} catch (e: IOException) {
throw RuntimeException(e)
@@ -103,7 +106,8 @@
parser.getAttributeWithName(ATTR_KEY) ?: return null,
parser.getAttributeWithName(ATTR_DESIRED_HEIGHT)?.toInt() ?: return null,
parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null,
- parser.getAttributeWithName(ATTR_TITLE)
+ parser.getAttributeWithName(ATTR_TITLE),
+ parser.getAttributeWithName(ATTR_TASK_ID)?.toInt() ?: INVALID_TASK_ID
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 1a4616c..6afcc06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -32,6 +32,7 @@
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.UserHandle;
import androidx.annotation.Nullable;
@@ -76,6 +77,7 @@
private final Context mContext;
private final Handler mMainHandler;
+ private final HandlerExecutor mHandlerExecutor;
private final MediaSessionManager mMediaSessionManager;
private MediaController mMediaController;
@@ -123,6 +125,7 @@
public PipMediaController(Context context, Handler mainHandler) {
mContext = context;
mMainHandler = mainHandler;
+ mHandlerExecutor = new HandlerExecutor(mMainHandler);
IntentFilter mediaControlFilter = new IntentFilter();
mediaControlFilter.addAction(ACTION_PLAY);
mediaControlFilter.addAction(ACTION_PAUSE);
@@ -247,8 +250,8 @@
*/
public void registerSessionListenerForCurrentUser() {
mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener);
- mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null,
- UserHandle.CURRENT, mMainHandler);
+ mMediaSessionManager.addOnActiveSessionsChangedListener(null, UserHandle.CURRENT,
+ mHandlerExecutor, mSessionsChangedListener);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index f8b4dd9..a0a76d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -98,7 +98,16 @@
PICTURE_IN_PICTURE_CHANGE_ASPECT_RATIO(609),
@UiEvent(doc = "User resize of the picture-in-picture window")
- PICTURE_IN_PICTURE_RESIZE(610);
+ PICTURE_IN_PICTURE_RESIZE(610),
+
+ @UiEvent(doc = "User unstashed picture-in-picture")
+ PICTURE_IN_PICTURE_STASH_UNSTASHED(709),
+
+ @UiEvent(doc = "User stashed picture-in-picture to the left side")
+ PICTURE_IN_PICTURE_STASH_LEFT(710),
+
+ @UiEvent(doc = "User stashed picture-in-picture to the right side")
+ PICTURE_IN_PICTURE_STASH_RIGHT(711);
private final int mId;
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 afc7b52..5e23281 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
@@ -170,6 +170,7 @@
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMenuController = menuController;
+ mPipUiEventLogger = pipUiEventLogger;
mMenuController.addListener(new PipMenuListener());
mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer,
@@ -186,6 +187,8 @@
() -> {
if (mPipBoundsState.isStashed()) {
animateToUnStashedState();
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_UNSTASHED);
mPipBoundsState.setStashed(STASH_TYPE_NONE);
} else {
mMenuController.showMenuWithPossibleDelay(MENU_STATE_FULL,
@@ -206,8 +209,6 @@
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
this::onAccessibilityShowMenu, this::updateMovementBounds, mainExecutor);
- mPipUiEventLogger = pipUiEventLogger;
-
mEnableStash = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_STASHING,
@@ -867,6 +868,8 @@
if (mEnableStash && shouldStash(vel, getPossiblyMotionBounds())) {
mMotionHelper.stashToEdge(vel.x, vel.y, this::stashEndAction /* endAction */);
} else {
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_UNSTASHED);
mPipBoundsState.setStashed(STASH_TYPE_NONE);
mMotionHelper.flingToSnapTarget(vel.x, vel.y,
this::flingEndAction /* endAction */);
@@ -897,6 +900,8 @@
if (!mTouchState.isWaitingForDoubleTap()) {
if (mPipBoundsState.isStashed()) {
animateToUnStashedState();
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_UNSTASHED);
mPipBoundsState.setStashed(STASH_TYPE_NONE);
} else {
// User has stalled long enough for this not to be a drag or a double tap,
@@ -921,9 +926,15 @@
&& mPipExclusionBoundsChangeListener.get() != null) {
mPipExclusionBoundsChangeListener.get().accept(mPipBoundsState.getBounds());
}
- if (mPipBoundsState.getBounds().left < 0) {
+ if (mPipBoundsState.getBounds().left < 0
+ && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT) {
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_LEFT);
mPipBoundsState.setStashed(STASH_TYPE_LEFT);
- } else {
+ } else if (mPipBoundsState.getBounds().left >= 0
+ && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT) {
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_RIGHT);
mPipBoundsState.setStashed(STASH_TYPE_RIGHT);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
new file mode 100644
index 0000000..78af9df
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
@@ -0,0 +1,71 @@
+/*
+ * 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.sizecompatui;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.RippleDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+/** Popup to show the hint about the {@link SizeCompatRestartButton}. */
+public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener {
+
+ private SizeCompatUILayout mLayout;
+
+ public SizeCompatHintPopup(Context context) {
+ super(context);
+ }
+
+ public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ void inject(SizeCompatUILayout layout) {
+ mLayout = layout;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ final Button gotItButton = findViewById(R.id.got_it);
+ gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
+ null /* content */, null /* mask */));
+ gotItButton.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ mLayout.dismissHint();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
index 9094d7d..08a8402 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
@@ -22,19 +22,13 @@
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.PopupWindow;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
/** Button to restart the size compat activity. */
@@ -42,10 +36,6 @@
View.OnLongClickListener {
private SizeCompatUILayout mLayout;
- private ImageButton mRestartButton;
- @VisibleForTesting
- PopupWindow mShowingHint;
- private WindowManager.LayoutParams mWinParams;
public SizeCompatRestartButton(@NonNull Context context) {
super(context);
@@ -67,24 +57,19 @@
void inject(SizeCompatUILayout layout) {
mLayout = layout;
- mWinParams = layout.getWindowLayoutParams();
- }
-
- void remove() {
- dismissHint();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mRestartButton = findViewById(R.id.size_compat_restart_button);
+ final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
final GradientDrawable mask = new GradientDrawable();
mask.setShape(GradientDrawable.OVAL);
mask.setColor(color);
- mRestartButton.setBackground(new RippleDrawable(color, null /* content */, mask));
- mRestartButton.setOnClickListener(this);
- mRestartButton.setOnLongClickListener(this);
+ restartButton.setBackground(new RippleDrawable(color, null /* content */, mask));
+ restartButton.setOnClickListener(this);
+ restartButton.setOnLongClickListener(this);
}
@Override
@@ -94,69 +79,7 @@
@Override
public boolean onLongClick(View v) {
- showHint();
+ mLayout.onRestartButtonLongClicked();
return true;
}
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mLayout.mShouldShowHint) {
- mLayout.mShouldShowHint = false;
- showHint();
- }
- }
-
- @Override
- public void setVisibility(@Visibility int visibility) {
- if (visibility == View.GONE && mShowingHint != null) {
- // Also dismiss the popup.
- dismissHint();
- }
- super.setVisibility(visibility);
- }
-
- @Override
- public void setLayoutDirection(int layoutDirection) {
- final int gravity = SizeCompatUILayout.getGravity(layoutDirection);
- if (mWinParams.gravity != gravity) {
- mWinParams.gravity = gravity;
- getContext().getSystemService(WindowManager.class).updateViewLayout(this,
- mWinParams);
- }
- super.setLayoutDirection(layoutDirection);
- }
-
- void showHint() {
- if (mShowingHint != null) {
- return;
- }
-
- // TODO: popup is not attached to the button surface. Need to handle this differently for
- // non-fullscreen task.
- final View popupView = LayoutInflater.from(getContext()).inflate(
- R.layout.size_compat_mode_hint, null);
- final PopupWindow popupWindow = new PopupWindow(popupView,
- LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
- popupWindow.setWindowLayoutType(mWinParams.type);
- popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation));
- popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
- popupWindow.setClippingEnabled(false);
- popupWindow.setOnDismissListener(() -> mShowingHint = null);
- mShowingHint = popupWindow;
-
- final Button gotItButton = popupView.findViewById(R.id.got_it);
- gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
- null /* content */, null /* mask */));
- gotItButton.setOnClickListener(view -> dismissHint());
- popupWindow.showAtLocation(mRestartButton, mWinParams.gravity, mLayout.mPopupOffsetX,
- mLayout.mPopupOffsetY);
- }
-
- void dismissHint() {
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- mShowingHint = null;
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index a3880f4..c981ade 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -50,7 +50,7 @@
/** Whether the IME is shown on display id. */
private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
- /** The showing buttons by task id. */
+ /** The showing UIs by task id. */
private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
/** Avoid creating display context frequently for non-default display. */
@@ -77,12 +77,12 @@
}
/**
- * Called when the Task info changed. Creates and updates the restart button if there is an
- * activity in size compat, or removes the restart button if there is no size compat activity.
+ * Called when the Task info changed. Creates and updates the size compat UI if there is an
+ * activity in size compat, or removes the UI if there is no size compat activity.
*
* @param displayId display the task and activity are in.
* @param taskId task the activity is in.
- * @param taskConfig task config to place the restart button with.
+ * @param taskConfig task config to place the size compat UI with.
* @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the
* top activity in this Task is not in size compat.
* @param taskListener listener to handle the Task Surface placement.
@@ -94,10 +94,10 @@
// Null token means the current foreground activity is not in size compatibility mode.
removeLayout(taskId);
} else if (mActiveLayouts.contains(taskId)) {
- // Button already exists, update the button layout.
+ // UI already exists, update the UI layout.
updateLayout(taskId, taskConfig, sizeCompatActivity, taskListener);
} else {
- // Create a new restart button.
+ // Create a new size compat UI.
createLayout(displayId, taskId, taskConfig, sizeCompatActivity, taskListener);
}
}
@@ -106,7 +106,7 @@
public void onDisplayRemoved(int displayId) {
mDisplayContextCache.remove(displayId);
- // Remove all buttons on the removed display.
+ // Remove all size compat UIs on the removed display.
final List<Integer> toRemoveTaskIds = new ArrayList<>();
forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId()));
for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) {
@@ -128,7 +128,7 @@
mDisplaysWithIme.remove(displayId);
}
- // Hide the button when input method is showing.
+ // Hide the size compat UIs when input method is showing.
forAllLayoutsOnDisplay(displayId, layout -> layout.updateImeVisibility(isShowing));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
index 5924b53..32f3648 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
@@ -30,7 +30,6 @@
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
-import android.view.Gravity;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
@@ -43,7 +42,7 @@
/**
* Records and handles layout of size compat UI on a task with size compat activity. Helps to
- * calculate proper bounds when configuration or button position changes.
+ * calculate proper bounds when configuration or UI position changes.
*/
class SizeCompatUILayout {
private static final String TAG = "SizeCompatUILayout";
@@ -56,12 +55,18 @@
private IBinder mActivityToken;
private ShellTaskOrganizer.TaskListener mTaskListener;
private DisplayLayout mDisplayLayout;
- @VisibleForTesting
- final SizeCompatUIWindowManager mWindowManager;
@VisibleForTesting
+ final SizeCompatUIWindowManager mButtonWindowManager;
+ @VisibleForTesting
+ @Nullable
+ SizeCompatUIWindowManager mHintWindowManager;
+ @VisibleForTesting
@Nullable
SizeCompatRestartButton mButton;
+ @VisibleForTesting
+ @Nullable
+ SizeCompatHintPopup mHint;
final int mButtonSize;
final int mPopupOffsetX;
final int mPopupOffsetY;
@@ -79,7 +84,7 @@
mTaskListener = taskListener;
mDisplayLayout = displayLayout;
mShouldShowHint = !hasShownHint;
- mWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
+ mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
mButtonSize =
mContext.getResources().getDimensionPixelSize(R.dimen.size_compat_button_size);
@@ -87,21 +92,52 @@
mPopupOffsetY = mButtonSize;
}
- /** Creates the button window. */
+ /** Creates the activity restart button window. */
void createSizeCompatButton(boolean isImeShowing) {
if (isImeShowing || mButton != null) {
// When ime is showing, wait until ime is dismiss to create UI.
return;
}
- mButton = mWindowManager.createSizeCompatUI();
- updateSurfacePosition();
+ mButton = mButtonWindowManager.createSizeCompatButton();
+ updateButtonSurfacePosition();
+
+ if (mShouldShowHint) {
+ // Only show by default for the first time.
+ mShouldShowHint = false;
+ createSizeCompatHint();
+ }
}
- /** Releases the button window. */
+ /** Creates the restart button hint window. */
+ private void createSizeCompatHint() {
+ if (mHint != null) {
+ // Hint already shown.
+ return;
+ }
+ mHintWindowManager = createHintWindowManager();
+ mHint = mHintWindowManager.createSizeCompatHint();
+ updateHintSurfacePosition();
+ }
+
+ @VisibleForTesting
+ SizeCompatUIWindowManager createHintWindowManager() {
+ return new SizeCompatUIWindowManager(mContext, mTaskConfig, this);
+ }
+
+ /** Dismisses the hint window. */
+ void dismissHint() {
+ mHint = null;
+ if (mHintWindowManager != null) {
+ mHintWindowManager.release();
+ mHintWindowManager = null;
+ }
+ }
+
+ /** Releases the UI windows. */
void release() {
- mButton.remove();
+ dismissHint();
mButton = null;
- mWindowManager.release();
+ mButtonWindowManager.release();
}
/** Called when size compat info changed. */
@@ -115,7 +151,10 @@
// Update configuration.
mContext = mContext.createConfigurationContext(taskConfig);
- mWindowManager.setConfiguration(taskConfig);
+ mButtonWindowManager.setConfiguration(taskConfig);
+ if (mHintWindowManager != null) {
+ mHintWindowManager.setConfiguration(taskConfig);
+ }
if (mButton == null || prevTaskListener != taskListener) {
// TaskListener changed, recreate the button for new surface parent.
@@ -126,14 +165,19 @@
if (!taskConfig.windowConfiguration.getBounds()
.equals(prevTaskConfig.windowConfiguration.getBounds())) {
- // Reposition the button surface.
- updateSurfacePosition();
+ // Reposition the UI surfaces.
+ updateButtonSurfacePosition();
+ updateHintSurfacePosition();
}
if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
// Update layout for RTL.
mButton.setLayoutDirection(taskConfig.getLayoutDirection());
- updateSurfacePosition();
+ updateButtonSurfacePosition();
+ if (mHint != null) {
+ mHint.setLayoutDirection(taskConfig.getLayoutDirection());
+ updateHintSurfacePosition();
+ }
}
}
@@ -149,8 +193,9 @@
displayLayout.getStableBounds(curStableBounds);
mDisplayLayout = displayLayout;
if (!prevStableBounds.equals(curStableBounds)) {
- // Stable bounds changed, update button surface position.
- updateSurfacePosition();
+ // Stable bounds changed, update UI surface positions.
+ updateButtonSurfacePosition();
+ updateHintSurfacePosition();
}
}
@@ -162,27 +207,46 @@
return;
}
+ // Hide size compat UIs when IME is showing.
final int newVisibility = isImeShowing ? View.GONE : View.VISIBLE;
if (mButton.getVisibility() != newVisibility) {
mButton.setVisibility(newVisibility);
}
+ if (mHint != null && mHint.getVisibility() != newVisibility) {
+ mHint.setVisibility(newVisibility);
+ }
}
/** Gets the layout params for restart button. */
- WindowManager.LayoutParams getWindowLayoutParams() {
+ WindowManager.LayoutParams getButtonWindowLayoutParams() {
final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ // Cannot be wrap_content as this determines the actual window size
mButtonSize, mButtonSize,
TYPE_APPLICATION_OVERLAY,
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
- winParams.gravity = getGravity(getLayoutDirection());
winParams.token = new Binder();
- winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + mContext.getDisplayId());
+ winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId());
winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
return winParams;
}
- /** Called when it is ready to be placed button surface button. */
+ /** Gets the layout params for hint popup. */
+ WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) {
+ final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ // Cannot be wrap_content as this determines the actual window size
+ hint.getMeasuredWidth(), hint.getMeasuredHeight(),
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ winParams.token = new Binder();
+ winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId());
+ winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ winParams.windowAnimations = android.R.style.Animation_InputMethod;
+ return winParams;
+ }
+
+ /** Called when it is ready to be placed size compat UI surface. */
void attachToParentSurface(SurfaceControl.Builder b) {
mTaskListener.attachChildSurfaceToTask(mTaskId, b);
}
@@ -192,13 +256,17 @@
ActivityClient.getInstance().restartActivityProcessIfVisible(mActivityToken);
}
+ /** Called when the restart button is long clicked. */
+ void onRestartButtonLongClicked() {
+ createSizeCompatHint();
+ }
+
@VisibleForTesting
- void updateSurfacePosition() {
- if (mButton == null || mWindowManager.getSurfaceControl() == null) {
+ void updateButtonSurfacePosition() {
+ if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
return;
}
- // The hint popup won't be at the correct position.
- mButton.dismissHint();
+ final SurfaceControl leash = mButtonWindowManager.getSurfaceControl();
// Use stable bounds to prevent the button from overlapping with system bars.
final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
@@ -212,8 +280,30 @@
: stableBounds.right - taskBounds.left - mButtonSize;
final int positionY = stableBounds.bottom - taskBounds.top - mButtonSize;
- mSyncQueue.runInSync(t ->
- t.setPosition(mWindowManager.getSurfaceControl(), positionX, positionY));
+ mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY));
+ }
+
+ void updateHintSurfacePosition() {
+ if (mHint == null || mHintWindowManager == null
+ || mHintWindowManager.getSurfaceControl() == null) {
+ return;
+ }
+ final SurfaceControl leash = mHintWindowManager.getSurfaceControl();
+
+ // Use stable bounds to prevent the hint from overlapping with system bars.
+ final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+ final Rect stableBounds = new Rect();
+ mDisplayLayout.getStableBounds(stableBounds);
+ stableBounds.intersect(taskBounds);
+
+ // Position of the hint in the container coordinate.
+ final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? stableBounds.left - taskBounds.left + mPopupOffsetX
+ : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth();
+ final int positionY =
+ stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight();
+
+ mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY));
}
int getDisplayId() {
@@ -227,9 +317,4 @@
private int getLayoutDirection() {
return mContext.getResources().getConfiguration().getLayoutDirection();
}
-
- static int getGravity(int layoutDirection) {
- return Gravity.BOTTOM
- | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
index a7ad982..f634c45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
@@ -24,12 +24,14 @@
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceSession;
+import android.view.View;
import android.view.WindowlessWindowManager;
import com.android.wm.shell.R;
/**
- * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton}.
+ * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or
+ * {@link SizeCompatHintPopup}.
*/
class SizeCompatUIWindowManager extends WindowlessWindowManager {
@@ -67,18 +69,39 @@
}
/** Inflates {@link SizeCompatRestartButton} on to the root surface. */
- SizeCompatRestartButton createSizeCompatUI() {
- if (mViewHost == null) {
- mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ SizeCompatRestartButton createSizeCompatButton() {
+ if (mViewHost != null) {
+ throw new IllegalStateException(
+ "A UI has already been created with this window manager.");
}
+ mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+
final SizeCompatRestartButton button = (SizeCompatRestartButton)
LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
button.inject(mLayout);
- mViewHost.setView(button, mLayout.getWindowLayoutParams());
+ mViewHost.setView(button, mLayout.getButtonWindowLayoutParams());
return button;
}
+ /** Inflates {@link SizeCompatHintPopup} on to the root surface. */
+ SizeCompatHintPopup createSizeCompatHint() {
+ if (mViewHost != null) {
+ throw new IllegalStateException(
+ "A UI has already been created with this window manager.");
+ }
+
+ mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+
+ final SizeCompatHintPopup hint = (SizeCompatHintPopup)
+ LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
+ // Measure how big the hint is.
+ hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ hint.inject(mLayout);
+ mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint));
+ return hint;
+ }
+
/** Releases the surface control and tears down the view hierarchy. */
void release() {
if (mViewHost != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 59f8c1d..2182ee5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -16,55 +16,78 @@
package com.android.wm.shell.transition;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
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 android.window.TransitionInfo.FLAG_TRANSLUCENT;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArrayMap;
+import android.view.Choreographer;
import android.view.SurfaceControl;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import com.android.internal.R;
+import com.android.internal.policy.AttributeCache;
+import com.android.internal.policy.TransitionAnimation;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
/** The default handler that handles anything not already handled. */
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
+ private static final int MAX_ANIMATION_DURATION = 3000;
+
private final TransactionPool mTransactionPool;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
+ private final TransitionAnimation mTransitionAnimation;
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
- DefaultTransitionHandler(@NonNull TransactionPool transactionPool,
+ private float mTransitionAnimationScaleSetting = 1.0f;
+
+ DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
mTransactionPool = transactionPool;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
+
+ AttributeCache.init(context);
}
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "start default transition animation, info = %s", info);
if (mAnimations.containsKey(transition)) {
throw new IllegalStateException("Got a duplicate startAnimation call for "
+ transition);
}
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
- final boolean isOpening = Transitions.isOpeningType(info.getType());
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
@@ -77,19 +100,9 @@
// Don't animate anything with an animating parent
if (change.getParent() != null) continue;
- final int mode = change.getMode();
- if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
- if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- // This received a transferred starting window, so don't animate
- continue;
- }
- // fade in
- startExampleAnimation(
- animations, change.getLeash(), true /* show */, onAnimFinish);
- } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) {
- // fade out
- startExampleAnimation(
- animations, change.getLeash(), false /* show */, onAnimFinish);
+ Animation a = loadAnimation(info.getType(), change);
+ if (a != null) {
+ startAnimInternal(animations, a, change.getLeash(), onAnimFinish);
}
}
t.apply();
@@ -105,32 +118,93 @@
return null;
}
- // TODO(shell-transitions): real animations
- private void startExampleAnimation(@NonNull ArrayList<Animator> animations,
- @NonNull SurfaceControl leash, boolean show, @NonNull Runnable finishCallback) {
- final float end = show ? 1.f : 0.f;
- final float start = 1.f - end;
+ @Override
+ public void setAnimScaleSetting(float scale) {
+ mTransitionAnimationScaleSetting = scale;
+ }
+
+ @Nullable
+ private Animation loadAnimation(int type, TransitionInfo.Change change) {
+ // TODO(b/178678389): It should handle more type animation here
+ Animation a = null;
+
+ final boolean isOpening = Transitions.isOpeningType(type);
+ final int mode = change.getMode();
+ final int flags = change.getFlags();
+
+ if (mode == TRANSIT_OPEN && isOpening) {
+ if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+ // This received a transferred starting window, so don't animate
+ return null;
+ }
+
+ if (change.getTaskInfo() != null) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_taskOpenEnterAnimation);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0
+ ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter);
+ }
+ } else if (mode == TRANSIT_TO_FRONT && isOpening) {
+ if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+ // This received a transferred starting window, so don't animate
+ return null;
+ }
+
+ a = mTransitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_taskToFrontEnterAnimation);
+ } else if (mode == TRANSIT_CLOSE && !isOpening) {
+ if (change.getTaskInfo() != null) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_taskCloseExitAnimation);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0
+ ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit);
+ }
+ } else if (mode == TRANSIT_TO_BACK && !isOpening) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_taskToBackExitAnimation);
+ } else if (mode == TRANSIT_CHANGE) {
+ // In the absence of a specific adapter, we just want to keep everything stationary.
+ a = new AlphaAnimation(1.f, 1.f);
+ a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
+ }
+
+ if (a != null) {
+ Rect start = change.getStartAbsBounds();
+ Rect end = change.getEndAbsBounds();
+ a.restrictDuration(MAX_ANIMATION_DURATION);
+ a.initialize(end.width(), end.height(), start.width(), start.height());
+ a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ }
+ return a;
+ }
+
+ private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim,
+ @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- final ValueAnimator va = ValueAnimator.ofFloat(start, end);
- va.setDuration(500);
+ final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+ final Transformation transformation = new Transformation();
+ final float[] matrix = new float[9];
+ // Animation length is already expected to be scaled.
+ va.overrideDurationScale(1.0f);
+ va.setDuration(anim.computeDurationHint());
va.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction);
- transaction.apply();
+ final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
+
+ applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix);
});
+
final Runnable finisher = () -> {
- transaction.setAlpha(leash, end);
- transaction.apply();
+ applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix);
+
mTransactionPool.release(transaction);
mMainExecutor.execute(() -> {
animations.remove(va);
finishCallback.run();
});
};
- va.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) { }
-
+ va.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finisher.run();
@@ -140,11 +214,17 @@
public void onAnimationCancel(Animator animation) {
finisher.run();
}
-
- @Override
- public void onAnimationRepeat(Animator animation) { }
});
animations.add(va);
mAnimExecutor.execute(va::start);
}
+
+ private static void applyTransformation(long time, SurfaceControl.Transaction t,
+ SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) {
+ anim.getTransformation(time, transformation);
+ t.setMatrix(leash, transformation.getMatrix(), matrix);
+ t.setAlpha(leash, transformation.getAlpha());
+ t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ t.apply();
+ }
}
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 1034489..89eee67 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
@@ -25,9 +25,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControl;
@@ -43,6 +47,7 @@
import androidx.annotation.BinderThread;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -63,6 +68,7 @@
SystemProperties.getBoolean("persist.debug.shell_transit", false);
private final WindowOrganizer mOrganizer;
+ private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionPlayerImpl mPlayerImpl;
@@ -72,6 +78,8 @@
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
+ private float mTransitionAnimationScaleSetting = 1.0f;
+
private static final class ActiveTransition {
TransitionHandler mFirstHandler = null;
}
@@ -84,26 +92,46 @@
}
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ @NonNull Context context, @NonNull ShellExecutor mainExecutor,
+ @NonNull ShellExecutor animExecutor) {
mOrganizer = organizer;
+ mContext = context;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
mPlayerImpl = new TransitionPlayerImpl();
// The very last handler (0 in the list) should be the default one.
- mHandlers.add(new DefaultTransitionHandler(pool, mainExecutor, animExecutor));
+ mHandlers.add(new DefaultTransitionHandler(pool, context, mainExecutor, animExecutor));
// Next lowest priority is remote transitions.
mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
mHandlers.add(mRemoteTransitionHandler);
+
+ ContentResolver resolver = context.getContentResolver();
+ mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ context.getResources().getFloat(
+ R.dimen.config_appTransitionAnimationDurationScaleDefault));
+ dispatchAnimScaleSetting(mTransitionAnimationScaleSetting);
+
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
+ new SettingsObserver());
}
private Transitions() {
mOrganizer = null;
+ mContext = null;
mMainExecutor = null;
mAnimExecutor = null;
mPlayerImpl = null;
mRemoteTransitionHandler = null;
}
+ private void dispatchAnimScaleSetting(float scale) {
+ for (int i = mHandlers.size() - 1; i >= 0; --i) {
+ mHandlers.get(i).setAnimScaleSetting(scale);
+ }
+ }
+
/** Create an empty/non-registering transitions object for system-ui tests. */
@VisibleForTesting
public static RemoteTransitions createEmptyForTesting() {
@@ -368,6 +396,13 @@
@Nullable
WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request);
+
+ /**
+ * Sets transition animation scale settings value to handler.
+ *
+ * @param scale The setting value of transition animation scale.
+ */
+ default void setAnimScaleSetting(float scale) {}
}
@BinderThread
@@ -404,4 +439,21 @@
});
}
}
+
+ private class SettingsObserver extends ContentObserver {
+
+ SettingsObserver() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ mTransitionAnimationScaleSetting = Settings.Global.getFloat(
+ mContext.getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE,
+ mTransitionAnimationScaleSetting);
+
+ mMainExecutor.execute(() -> dispatchAnimScaleSetting(mTransitionAnimationScaleSetting));
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index a57ac35..9dd25fe 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "WMShellFlickerTests",
srcs: ["src/**/*.java", "src/**/*.kt"],
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 3282ece..10aea51 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,239 +18,92 @@
import android.graphics.Region
import android.view.Surface
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-@JvmOverloads
-fun LayersAssertionBuilder.appPairsDividerIsVisible(bugId: Int = 0) {
- end("appPairsDividerIsVisible", bugId) {
+fun FlickerTestParameter.appPairsDividerIsVisible() {
+ assertLayersEnd {
this.isVisible(APP_PAIR_SPLIT_DIVIDER)
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.appPairsDividerIsInvisible(bugId: Int = 0) {
- end("appPairsDividerIsInVisible", bugId) {
+fun FlickerTestParameter.appPairsDividerIsInvisible() {
+ assertLayersEnd {
this.notExists(APP_PAIR_SPLIT_DIVIDER)
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.appPairsDividerBecomesVisible(bugId: Int = 0) {
- all("dividerLayerBecomesVisible", bugId) {
+fun FlickerTestParameter.appPairsDividerBecomesVisible() {
+ assertLayers {
this.hidesLayer(DOCKED_STACK_DIVIDER)
.then()
.showsLayer(DOCKED_STACK_DIVIDER)
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerIsVisible(bugId: Int = 0) {
- end("dockedStackDividerIsVisible", bugId) {
+fun FlickerTestParameter.dockedStackDividerIsVisible() {
+ assertLayersEnd {
this.isVisible(DOCKED_STACK_DIVIDER)
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(bugId: Int = 0) {
- all("dividerLayerBecomesVisible", bugId) {
+fun FlickerTestParameter.dockedStackDividerBecomesVisible() {
+ assertLayers {
this.hidesLayer(DOCKED_STACK_DIVIDER)
.then()
.showsLayer(DOCKED_STACK_DIVIDER)
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(bugId: Int = 0) {
- all("dividerLayerBecomesInvisible", bugId) {
+fun FlickerTestParameter.dockedStackDividerBecomesInvisible() {
+ assertLayers {
this.showsLayer(DOCKED_STACK_DIVIDER)
.then()
.hidesLayer(DOCKED_STACK_DIVIDER)
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerIsInvisible(bugId: Int = 0) {
- end("dockedStackDividerIsInvisible", bugId) {
+fun FlickerTestParameter.dockedStackDividerIsInvisible() {
+ assertLayersEnd {
this.notExists(DOCKED_STACK_DIVIDER)
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible(
- rotation: Int,
- primaryLayerName: String,
- bugId: Int = 0
-) {
- end("PrimaryAppBounds", bugId) {
+fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) {
+ assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisible(
rotation: Int,
- secondaryLayerName: String,
- bugId: Int = 0
+ primaryLayerName: String
) {
- end("SecondaryAppBounds", bugId) {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible(
- rotation: Int,
- primaryLayerName: String,
- bugId: Int = 0
-) {
- end("PrimaryAppBounds", bugId) {
+ assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible(
+fun FlickerTestParameter.appPairsSecondaryBoundsIsVisible(
rotation: Int,
- secondaryLayerName: String,
- bugId: Int = 0
+ secondaryLayerName: String
) {
- end("SecondaryAppBounds", bugId) {
- val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.appPairsDividerIsVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- end("appPairsDividerIsVisible", bugId, enabled) {
- this.isVisible(APP_PAIR_SPLIT_DIVIDER)
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.appPairsDividerIsInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- end("appPairsDividerIsInVisible", bugId, enabled) {
- this.notExists(APP_PAIR_SPLIT_DIVIDER)
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.appPairsDividerBecomesVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("dividerLayerBecomesVisible", bugId, enabled) {
- this.hidesLayer(DOCKED_STACK_DIVIDER)
- .then()
- .showsLayer(DOCKED_STACK_DIVIDER)
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.dockedStackDividerIsVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- end("dockedStackDividerIsVisible", bugId, enabled) {
- this.isVisible(DOCKED_STACK_DIVIDER)
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("dividerLayerBecomesVisible", bugId, enabled) {
- this.hidesLayer(DOCKED_STACK_DIVIDER)
- .then()
- .showsLayer(DOCKED_STACK_DIVIDER)
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("dividerLayerBecomesInvisible", bugId, enabled) {
- this.showsLayer(DOCKED_STACK_DIVIDER)
- .then()
- .hidesLayer(DOCKED_STACK_DIVIDER)
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.dockedStackDividerIsInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- end("dockedStackDividerIsInvisible", bugId, enabled) {
- this.notExists(DOCKED_STACK_DIVIDER)
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.appPairsPrimaryBoundsIsVisible(
- rotation: Int,
- primaryLayerName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- end("PrimaryAppBounds", bugId, enabled) {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.appPairsSecondaryBoundsIsVisible(
- rotation: Int,
- secondaryLayerName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- end("SecondaryAppBounds", bugId, enabled) {
+ assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
}
}
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.dockedStackPrimaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisible(
rotation: Int,
- primaryLayerName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
+ secondaryLayerName: String
) {
- end("PrimaryAppBounds", bugId, enabled) {
- val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilderLegacy.dockedStackSecondaryBoundsIsVisible(
- rotation: Int,
- secondaryLayerName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- end("SecondaryAppBounds", bugId, enabled) {
+ assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
}
@@ -260,10 +113,10 @@
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
Region(0, 0, displayBounds.bounds.right,
- dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset)
+ dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset)
} else {
Region(0, 0, dividerRegion.bounds.left,
- dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset)
+ dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset)
}
}
@@ -271,12 +124,12 @@
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
Region(0,
- dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset)
+ dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.bounds.right,
+ displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset)
} else {
Region(dividerRegion.bounds.right, 0,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset)
+ displayBounds.bounds.right,
+ displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset)
}
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
index 89bbdb0..9c50630 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
@@ -16,33 +16,33 @@
package com.android.wm.shell.flicker
+import android.app.Instrumentation
import android.content.pm.PackageManager
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
-import android.os.RemoteException
-import android.os.SystemClock
-import android.platform.helpers.IAppHelper
import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.Flicker
import org.junit.Assume.assumeFalse
import org.junit.Before
+import org.junit.runners.Parameterized
/**
* Base class of all Flicker test that performs common functions for all flicker tests:
*
- *
* - Caches transitions so that a transition is run once and the transition results are used by
* tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
* multiple times.
* - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
* - Fails tests if results are not available for any test due to jank.
*/
-abstract class FlickerTestBase {
- val instrumentation by lazy { InstrumentationRegistry.getInstrumentation() }
- val uiDevice by lazy { UiDevice.getInstance(instrumentation) }
- val packageManager: PackageManager by lazy { instrumentation.context.getPackageManager() }
+abstract class FlickerTestBase(
+ protected val rotationName: String,
+ protected val rotation: Int
+) {
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val uiDevice = UiDevice.getInstance(instrumentation)
+ val packageManager: PackageManager = instrumentation.context.packageManager
protected val isTelevision: Boolean by lazy {
packageManager.run {
hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
@@ -56,83 +56,12 @@
@Before
open fun televisionSetUp() = assumeFalse(isTelevision)
- /**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param rotation Initial screen rotation
- *
- * @return test tag with pattern <NAME>__<APP>__<ROTATION>
- </ROTATION></APP></NAME> */
- protected fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String {
- return buildTestTag(
- testName, app, rotation, rotation, app2 = null, extraInfo = "")
- }
-
- /**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param beginRotation Initial screen rotation
- * @param endRotation End screen rotation (if any, otherwise use same as initial)
- *
- * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
- </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
- protected fun buildTestTag(
- testName: String,
- app: IAppHelper,
- beginRotation: Int,
- endRotation: Int
- ): String {
- return buildTestTag(
- testName, app, beginRotation, endRotation, app2 = null, extraInfo = "")
- }
-
- /**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param app2 Second app being launched (if any)
- * @param beginRotation Initial screen rotation
- * @param endRotation End screen rotation (if any, otherwise use same as initial)
- * @param extraInfo Additional information to append to the tag
- *
- * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
- </EXTRA></NAME> */
- protected fun buildTestTag(
- testName: String,
- app: IAppHelper,
- beginRotation: Int,
- endRotation: Int,
- app2: IAppHelper?,
- extraInfo: String
- ): String {
- var testTag = "${testName}__${app.launcherName}"
- if (app2 != null) {
- testTag += "-${app2.launcherName}"
- }
- testTag += "__${Surface.rotationToString(beginRotation)}"
- if (endRotation != beginRotation) {
- testTag += "-${Surface.rotationToString(endRotation)}"
- }
- if (extraInfo.isNotEmpty()) {
- testTag += "__$extraInfo"
- }
- return testTag
- }
-
- protected fun Flicker.setRotation(rotation: Int) {
- try {
- when (rotation) {
- Surface.ROTATION_270 -> device.setOrientationLeft()
- Surface.ROTATION_90 -> device.setOrientationRight()
- Surface.ROTATION_0 -> device.setOrientationNatural()
- else -> device.setOrientationNatural()
- }
- // Wait for animation to complete
- SystemClock.sleep(1000)
- } catch (e: RemoteException) {
- throw RuntimeException(e)
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt
deleted file mode 100644
index 90334ae..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker
-
-import android.view.Surface
-import org.junit.runners.Parameterized
-
-abstract class NonRotationTestBase(
- protected val rotationName: String,
- protected val rotation: Int
-) : FlickerTestBase() {
- companion object {
- const val SCREENSHOT_LAYER = "RotationLayer"
-
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index c3fd663..5d51b2f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -18,15 +18,16 @@
import android.os.Bundle
import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -41,47 +42,47 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AppPairsTestCannotPairNonResizeableApps(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : AppPairsTransition(testSpec) {
+
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = {
+ super.transition(this, it)
+ transitions {
+ nonResizeableApp?.launchViaIntent(wmHelper)
+ // TODO pair apps through normal UX flow
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+
+ @Presubmit
+ @Test
+ fun onlyResizeableAppWindowVisible() {
+ val nonResizeableApp = nonResizeableApp
+ require(nonResizeableApp != null) {
+ "Non resizeable app not initialized"
+ }
+ testSpec.assertWmEnd {
+ isVisible(nonResizeableApp.defaultWindowName)
+ isInvisible(primaryApp.defaultWindowName)
+ }
+ }
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag(configuration)
- }
- transitions {
- nonResizeableApp?.launchViaIntent(wmHelper)
- // TODO pair apps through normal UX flow
- executeShellCommand(
- composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
- }
- assertions {
- presubmit {
- layersTrace {
- appPairsDividerIsInvisible()
- }
- windowManagerTrace {
- val nonResizeableApp = nonResizeableApp
- require(nonResizeableApp != null) {
- "Non resizeable app not initialized"
- }
-
- end("onlyResizeableAppWindowVisible") {
- isVisible(nonResizeableApp.defaultWindowName)
- isInvisible(primaryApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- transition, testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS)
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = AppPairsHelper.TEST_REPETITIONS)
}
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 7a2a5e4..77890ba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -18,17 +18,19 @@
import android.os.Bundle
import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -39,52 +41,53 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AppPairsTestPairPrimaryAndSecondaryApps(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : AppPairsTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = {
+ super.transition(this, it)
+ transitions {
+ // TODO pair apps through normal UX flow
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+
+ @Presubmit
+ @Test
+ fun bothAppWindowsVisible() {
+ testSpec.assertWmEnd {
+ isVisible(primaryApp.defaultWindowName)
+ isVisible(secondaryApp.defaultWindowName)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun appsEndingBounds() {
+ testSpec.assertLayersEnd {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ this.hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
+ }
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag(configuration)
- }
- transitions {
- // TODO pair apps through normal UX flow
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
- }
- assertions {
- presubmit {
- layersTrace {
- appPairsDividerIsVisible()
- }
- windowManagerTrace {
- end("bothAppWindowsVisible") {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
- }
- }
- }
-
- flaky {
- layersTrace {
- end("appsEndingBounds") {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.hasVisibleRegion(primaryApp.defaultWindowName,
- appPairsHelper.getPrimaryBounds(dividerRegion))
- .hasVisibleRegion(secondaryApp.defaultWindowName,
- appPairsHelper.getSecondaryBounds(dividerRegion))
- }
- }
- }
- }
- }
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, transition,
- testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS)
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = AppPairsHelper.TEST_REPETITIONS)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index d8dc4c2..3d3ca0c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -18,17 +18,19 @@
import android.os.Bundle
import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -39,61 +41,67 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AppPairsTestUnpairPrimaryAndSecondaryApps(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : AppPairsTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = {
+ super.transition(this, it)
+ setup {
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ transitions {
+ // TODO pair apps through normal UX flow
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, secondaryTaskId, pair = false))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+
+ @Presubmit
+ @Test
+ fun bothAppWindowsInvisible() {
+ testSpec.assertWmEnd {
+ isInvisible(primaryApp.defaultWindowName)
+ isInvisible(secondaryApp.defaultWindowName)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun appsStartingBounds() {
+ testSpec.assertLayersStart {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun appsEndingBounds() {
+ testSpec.assertLayersEnd {
+ notExists(primaryApp.defaultWindowName)
+ notExists(secondaryApp.defaultWindowName)
+ }
+ }
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag(configuration)
- }
- setup {
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
- }
- transitions {
- // TODO pair apps through normal UX flow
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = false))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
- }
- assertions {
- presubmit {
- layersTrace {
- appPairsDividerIsInvisible()
- }
- windowManagerTrace {
- end("bothAppWindowsInvisible") {
- isInvisible(primaryApp.defaultWindowName)
- isInvisible(secondaryApp.defaultWindowName)
- }
- }
- }
-
- flaky {
- layersTrace {
- start("appsStartingBounds") {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.hasVisibleRegion(primaryApp.defaultWindowName,
- appPairsHelper.getPrimaryBounds(dividerRegion))
- .hasVisibleRegion(secondaryApp.defaultWindowName,
- appPairsHelper.getSecondaryBounds(dividerRegion))
- }
- end("appsEndingBounds") {
- this.notExists(primaryApp.defaultWindowName)
- .notExists(secondaryApp.defaultWindowName)
- }
- }
- }
- }
- }
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, transition,
- testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS)
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = AppPairsHelper.TEST_REPETITIONS)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 78a938a..9e6752d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -18,38 +18,53 @@
import android.app.Instrumentation
import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.system.helpers.ActivityHelper
import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isRotated
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.navBarWindowIsAlwaysVisible
+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.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.testapp.Components
+import org.junit.Test
import java.io.IOException
-open class AppPairsTransition(
- protected val instrumentation: Instrumentation
-) {
- internal val activityHelper = ActivityHelper.getInstance()
-
- internal val appPairsHelper = AppPairsHelper(instrumentation,
+abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val isRotated = testSpec.config.startRotation.isRotated()
+ protected val activityHelper = ActivityHelper.getInstance()
+ protected val appPairsHelper = AppPairsHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
Components.SplitScreenActivity.COMPONENT)
- internal val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
- internal val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
- internal open val nonResizeableApp: SplitScreenHelper? =
+ protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
+ protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
+ protected open val nonResizeableApp: SplitScreenHelper? =
SplitScreenHelper.getNonResizeable(instrumentation)
- internal var primaryTaskId = ""
- internal var secondaryTaskId = ""
- internal var nonResizeableTaskId = ""
+ protected var primaryTaskId = ""
+ protected var secondaryTaskId = ""
+ protected var nonResizeableTaskId = ""
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ transition(this, testSpec.config)
+ }
+ }
internal open val transition: FlickerBuilder.(Bundle) -> Unit
get() = { configuration ->
@@ -71,20 +86,9 @@
primaryTaskId, secondaryTaskId, pair = false))
executeShellCommand(composePairsCommand(
primaryTaskId, nonResizeableTaskId, pair = false))
- primaryApp.exit()
- secondaryApp.exit()
- nonResizeableApp?.exit()
- }
- }
-
- assertions {
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ nonResizeableApp?.exit(wmHelper)
}
}
}
@@ -128,4 +132,20 @@
}
append("$primaryApp $secondaryApp")
}
+
+ @Presubmit
+ @Test
+ open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 8aee005..35a0020 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -18,25 +18,25 @@
import android.os.Bundle
import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -47,57 +47,65 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class RotateTwoLaunchedAppsInAppPairsMode(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : RotateTwoLaunchedAppsTransition(
- InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : RotateTwoLaunchedAppsTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = {
+ super.transition(this, it)
+ transitions {
+ executeShellCommand(composePairsCommand(
+ primaryTaskId, secondaryTaskId, true /* pair */))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ setRotation(testSpec.config.endRotation)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun bothAppWindowsVisible() {
+ testSpec.assertWmEnd {
+ isVisible(primaryApp.defaultWindowName)
+ .isVisible(secondaryApp.defaultWindowName)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ testSpec.config.endRotation)
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
+ testSpec.config.endRotation)
+
+ @FlakyTest(bugId = 172776659)
+ @Test
+ fun appPairsPrimaryBoundsIsVisible() =
+ testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
+ primaryApp.defaultWindowName)
+
+ @FlakyTest(bugId = 172776659)
+ @Test
+ fun appPairsSecondaryBoundsIsVisible() =
+ testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
+ secondaryApp.defaultWindowName)
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag(configuration)
- }
- transitions {
- executeShellCommand(composePairsCommand(
- primaryTaskId, secondaryTaskId, true /* pair */))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
- setRotation(configuration.endRotation)
- }
- assertions {
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("bothAppWindowsVisible") {
- isVisible(primaryApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
- }
- }
- }
-
- flaky {
- layersTrace {
- appPairsDividerIsVisible()
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- appPairsPrimaryBoundsIsVisible(configuration.endRotation,
- primaryApp.defaultWindowName, bugId = 172776659)
- appPairsSecondaryBoundsIsVisible(configuration.endRotation,
- secondaryApp.defaultWindowName, bugId = 172776659)
- }
- }
- }
- }
-
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- transition, testSpec,
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270))
+ supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)
+ )
}
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index bc99c94..326a775 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -20,18 +20,16 @@
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.appPairsDividerIsVisible
@@ -39,7 +37,9 @@
import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -48,70 +48,84 @@
* Test open apps to app pairs and rotate.
* To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsRotateAndEnterAppPairsMode`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : RotateTwoLaunchedAppsTransition(
- InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : RotateTwoLaunchedAppsTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = {
+ super.transition(this, it)
+ transitions {
+ this.setRotation(testSpec.config.endRotation)
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
+ testSpec.config.endRotation)
+
+ @Presubmit
+ @Test
+ fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(isRotated)
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(isRotated)
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @Presubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun bothAppWindowsVisible() {
+ testSpec.assertWmEnd {
+ isVisible(primaryApp.defaultWindowName)
+ isVisible(secondaryApp.defaultWindowName)
+ }
+ }
+
+ @FlakyTest(bugId = 172776659)
+ @Test
+ fun appPairsPrimaryBoundsIsVisible() =
+ testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
+ primaryApp.defaultWindowName)
+
+ @FlakyTest(bugId = 172776659)
+ @Test
+ fun appPairsSecondaryBoundsIsVisible() =
+ testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
+ secondaryApp.defaultWindowName)
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag(configuration)
- }
- transitions {
- this.setRotation(configuration.endRotation)
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
- presubmit {
- layersTrace {
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- appPairsDividerIsVisible()
- if (!isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- }
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("bothAppWindowsVisible") {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
- }
- }
- }
- flaky {
- layersTrace {
- appPairsPrimaryBoundsIsVisible(configuration.endRotation,
- primaryApp.defaultWindowName, 172776659)
- appPairsSecondaryBoundsIsVisible(configuration.endRotation,
- secondaryApp.defaultWindowName, 172776659)
-
- if (isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- }
- }
- }
- }
- }
-
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- transition, testSpec,
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)
)
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
index 8ea2544..271b25f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
@@ -16,17 +16,17 @@
package com.android.wm.shell.flicker.apppairs
-import android.app.Instrumentation
import android.os.Bundle
import android.view.Surface
+import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-open class RotateTwoLaunchedAppsTransition(
- instrumentation: Instrumentation
-) : AppPairsTransition(instrumentation) {
+abstract class RotateTwoLaunchedAppsTransition(
+ testSpec: FlickerTestParameter
+) : AppPairsTransition(testSpec) {
override val nonResizeableApp: SplitScreenHelper?
get() = null
@@ -45,8 +45,8 @@
eachRun {
executeShellCommand(composePairsCommand(
primaryTaskId, secondaryTaskId, pair = false))
- primaryApp.exit()
- secondaryApp.exit()
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 9f2087f..901b7a3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -18,7 +18,6 @@
import android.app.Instrumentation
import android.content.ComponentName
-import android.os.SystemClock
import com.android.wm.shell.flicker.testapp.Components
class SplitScreenHelper(
@@ -27,17 +26,6 @@
componentsInfo: ComponentName
) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
- /**
- * Reopens the first device window from the list of recent apps (overview)
- */
- fun reopenAppFromOverview() {
- val x = uiDevice.displayWidth / 2
- val y = uiDevice.displayHeight / 2
- uiDevice.click(x, y)
- // Wait for animation to complete.
- SystemClock.sleep(TIMEOUT_MS)
- }
-
companion object {
const val TEST_REPETITIONS = 1
const val TIMEOUT_MS = 3_000L
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 2c29220..9b70fac 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
@@ -19,13 +19,13 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.buildTestTag
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.startRotation
@@ -36,6 +36,7 @@
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -47,56 +48,74 @@
@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
// @FlakyTest(bugId = 179116910)
class EnterSplitScreenDockActivity(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ transitions {
+ device.launchSplitScreen(wmHelper)
+ }
+ }
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun dockedStackPrimaryBoundsIsVisible() =
+ testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
+ splitScreenApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible()
+
+ @FlakyTest(bugId = 178531736)
+ @Test
+ // b/178531736
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME,
+ WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
+ splitScreenApp.defaultWindowName)
+ )
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @FlakyTest(bugId = 178531736)
+ @Test
+ // b/178531736
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME,
+ WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
+ splitScreenApp.defaultWindowName)
+ )
+
+ @Presubmit
+ @Test
+ fun appWindowIsVisible() {
+ testSpec.assertWmEnd {
+ isVisible(splitScreenApp.defaultWindowName)
+ }
+ }
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testLegacySplitScreenDockActivity", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- device.launchSplitScreen()
- }
- assertions {
- layersTrace {
- dockedStackPrimaryBoundsIsVisible(
- configuration.startRotation,
- splitScreenApp.defaultWindowName, bugId = 169271943)
- dockedStackDividerBecomesVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME,
- WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
- splitScreenApp.defaultWindowName),
- bugId = 178531736
- )
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME,
- WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
- splitScreenApp.defaultWindowName),
- bugId = 178531736
- )
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, defaultTransitionSetup, testSpec,
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
)
}
}
-}
\ No newline at end of file
+}
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 903971e..bd57a59 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
@@ -19,14 +19,15 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.launchSplitScreen
+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
@@ -37,6 +38,7 @@
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -45,61 +47,79 @@
* Test open activity to primary split screen and dock secondary activity to side
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenLaunchToSide`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EnterSplitScreenLaunchToSide(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ transitions {
+ device.launchSplitScreen(wmHelper)
+ device.reopenAppFromOverview(wmHelper)
+ }
+ }
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun dockedStackPrimaryBoundsIsVisible() =
+ testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
+ splitScreenApp.defaultWindowName)
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun dockedStackSecondaryBoundsIsVisible() =
+ testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
+ secondaryApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ // b/169271943
+ fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible()
+
+ @FlakyTest(bugId = 178447631)
+ @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)
+ )
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME,
+ splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName)
+ )
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testLegacySplitScreenLaunchToSide", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- device.launchSplitScreen()
- secondaryApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- dockedStackPrimaryBoundsIsVisible(
- configuration.startRotation,
- splitScreenApp.defaultWindowName, bugId = 169271943)
- dockedStackSecondaryBoundsIsVisible(
- configuration.startRotation,
- secondaryApp.defaultWindowName, bugId = 169271943)
- dockedStackDividerBecomesVisible()
- // TODO(b/178447631) Remove Splash Screen from white list when flicker lib
- // add a wait for splash screen be gone
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- appWindowBecomesVisible(secondaryApp.defaultWindowName)
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
- }
- }
- }
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, defaultTransitionSetup, testSpec,
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842
)
}
}
-}
\ No newline at end of file
+}
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 e361923..67578b2 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
@@ -17,16 +17,14 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.os.Bundle
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.buildTestTag
import com.android.server.wm.flicker.helpers.canSplitScreen
import com.android.server.wm.flicker.helpers.openQuickstep
import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
@@ -35,6 +33,7 @@
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.Assert
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -43,59 +42,68 @@
* Test open non-resizable activity will auto exit split screen mode
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenNonResizableNotDock`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FlakyTest(bugId = 173875043)
class EnterSplitScreenNonResizableNotDock(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testLegacySplitScreenNonResizeableActivityNotDock", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- nonResizeableApp.launchViaIntent(wmHelper)
- device.openQuickstep()
- if (device.canSplitScreen()) {
- Assert.fail("Non-resizeable app should not enter split screen")
- }
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME,
- SPLASH_SCREEN_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(WALLPAPER_TITLE,
- LAUNCHER_PACKAGE_NAME,
- SPLASH_SCREEN_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
- )
- end("appWindowIsVisible") {
- isInvisible(nonResizeableApp.defaultWindowName)
- }
- }
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ teardown {
+ eachRun {
+ nonResizeableApp.exit(wmHelper)
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, defaultTransitionSetup, testSpec,
+ transitions {
+ nonResizeableApp.launchViaIntent(wmHelper)
+ device.openQuickstep(wmHelper)
+ if (device.canSplitScreen(wmHelper)) {
+ Assert.fail("Non-resizeable app should not enter split screen")
+ }
+ }
+ }
+
+ @Test
+ fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+
+ @FlakyTest(bugId = 178447631)
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME,
+ SPLASH_SCREEN_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+ )
+
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(WALLPAPER_TITLE,
+ LAUNCHER_PACKAGE_NAME,
+ SPLASH_SCREEN_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+ )
+
+ @Test
+ fun appWindowIsVisible() {
+ testSpec.assertWmEnd {
+ isInvisible(nonResizeableApp.defaultWindowName)
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
}
}
}
\ No newline at end of file
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 4933665..5d42a4a 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
@@ -19,14 +19,14 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesInVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.layerBecomesInvisible
@@ -36,6 +36,7 @@
import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -44,51 +45,72 @@
* Test open resizeable activity split in primary, and drag divider to bottom exit split screen
* To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenFromBottom`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ExitLegacySplitScreenFromBottom(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testExitLegacySplitScreenFromBottom", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- device.launchSplitScreen()
- device.exitSplitScreenFromBottom()
- }
- assertions {
- layersTrace {
- layerBecomesInvisible(DOCKED_STACK_DIVIDER)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- appWindowBecomesInVisible(secondaryApp.defaultWindowName)
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName),
- bugId = 178447631
- )
- }
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ setup {
+ eachRun {
+ splitScreenApp.launchViaIntent(wmHelper)
+ device.launchSplitScreen(wmHelper)
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, defaultTransitionSetup, testSpec,
+ teardown {
+ eachRun {
+ splitScreenApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ device.exitSplitScreenFromBottom()
+ }
+ }
+
+ @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)
+ )
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesInVisible() =
+ testSpec.appWindowBecomesInVisible(secondaryApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @FlakyTest(bugId = 178447631)
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName)
+ )
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842
+ supportedRotations = listOf(Surface.ROTATION_0) // b/175687842
)
}
}
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 ff3a979..ff8f9c6 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
@@ -17,15 +17,17 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesInVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.buildTestTag
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.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
@@ -34,6 +36,7 @@
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -45,49 +48,71 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ExitPrimarySplitScreenShowSecondaryFullscreen(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testExitPrimarySplitScreenShowSecondaryFullscreen", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- device.launchSplitScreen()
- secondaryApp.reopenAppFromOverview()
- // TODO(b/175687842) Can not find Split screen divider, use exit() instead
- splitScreenApp.exit()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible(bugId = 175687842)
- layerBecomesInvisible(splitScreenApp.defaultWindowName)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName),
- bugId = 178447631
- )
- }
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ teardown {
+ eachRun {
+ secondaryApp.exit(wmHelper)
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, defaultTransitionSetup, testSpec,
+ transitions {
+ splitScreenApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ device.launchSplitScreen(wmHelper)
+ device.reopenAppFromOverview(wmHelper)
+ // TODO(b/175687842) Can not find Split screen divider, use exit() instead
+ splitScreenApp.exit(wmHelper)
+ }
+ }
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+
+ @Presubmit
+ @Test
+ fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+
+ @FlakyTest(bugId = 178447631)
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName)
+ )
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesInVisible() =
+ testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @FlakyTest(bugId = 178447631)
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName)
+ )
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
)
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
new file mode 100644
index 0000000..893b101
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.view.Surface
+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
+
+abstract class LegacySplitScreenRotateTransition(
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = {
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ splitScreenApp.launchViaIntent(wmHelper)
+ }
+ }
+ teardown {
+ eachRun {
+ splitScreenApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
+}
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 03b6edf..09a7e31 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
@@ -16,18 +16,19 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.focusDoesNotChange
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isInSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
@@ -39,13 +40,13 @@
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.repetitions
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -54,81 +55,100 @@
* Test open app to split screen.
* To run this test: `atest WMShellFlickerTests:LegacySplitScreenToLauncher`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class LegacySplitScreenToLauncher(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ private val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation)
+ .launcherStrategy.supportedLauncherPackage
+ private val testApp = SimpleAppHelper(instrumentation)
+
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
+ }
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ this.setRotation(configuration.endRotation)
+ device.launchSplitScreen(wmHelper)
+ device.waitForIdle()
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ device.exitSplitScreen()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation)
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation)
+
+ @Presubmit
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(launcherPackageName))
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(testApp.getPackage())
+
+ @FlakyTest(bugId = 151179149)
+ @Test
+ fun focusDoesNotChange() = testSpec.focusDoesNotChange()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation)
- .launcherStrategy.supportedLauncherPackage
- val testApp = SimpleAppHelper(instrumentation)
-
+ fun getParams(): Collection<FlickerTestParameter> {
// b/161435597 causes the test not to work on 90 degrees
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
- withTestName {
- buildTestTag("splitScreenToLauncher", configuration)
- }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.endRotation)
- device.launchSplitScreen()
- device.waitForIdle()
- }
- }
- teardown {
- eachRun {
- testApp.exit()
- }
- test {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- }
- }
- transitions {
- device.exitSplitScreen()
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.endRotation)
- navBarLayerRotatesAndScales(configuration.endRotation)
- statusBarLayerRotatesScales(configuration.endRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(launcherPackageName))
-
- // b/161435597 causes the test not to work on 90 degrees
- dockedStackDividerBecomesInvisible()
-
- layerBecomesInvisible(testApp.getPackage())
- }
-
- eventLog {
- focusDoesNotChange(bugId = 151179149)
- }
- }
- }
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0))
}
}
}
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 328ff88..6ab1f0b 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
@@ -20,33 +20,33 @@
import android.os.Bundle
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isRotated
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 com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-abstract class LegacySplitScreenTransition(
- protected val instrumentation: Instrumentation
-) {
- internal val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
- internal val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
- internal val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
- internal val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
+abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val isRotated = testSpec.config.startRotation.isRotated()
+ protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
+ protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
+ protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
+ protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
.launcherStrategy.supportedLauncherPackage
- internal val LIVE_WALLPAPER_PACKAGE_NAME =
- "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
- internal val LETTERBOX_NAME = "Letterbox"
- internal val TOAST_NAME = "Toast"
- internal val SPLASH_SCREEN_NAME = "Splash Screen"
- internal open val defaultTransitionSetup: FlickerBuilder.(Bundle) -> Unit
+ protected open val transition: FlickerBuilder.(Bundle) -> Unit
get() = { configuration ->
setup {
eachRun {
device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview()
+ device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
splitScreenApp.launchViaIntent(wmHelper)
this.setRotation(configuration.startRotation)
@@ -54,46 +54,46 @@
}
teardown {
eachRun {
- splitScreenApp.exit()
- secondaryApp.exit()
+ secondaryApp.exit(wmHelper)
+ splitScreenApp.exit(wmHelper)
this.setRotation(Surface.ROTATION_0)
}
}
}
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ transition(this, testSpec.config)
+ }
+ }
+
internal open val cleanSetup: FlickerBuilder.(Bundle) -> Unit
get() = { configuration ->
setup {
eachRun {
device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview()
+ device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
this.setRotation(configuration.startRotation)
}
}
teardown {
eachRun {
- nonResizeableApp.exit()
+ nonResizeableApp.exit(wmHelper)
+ splitScreenApp.exit(wmHelper)
+ device.pressHome()
this.setRotation(Surface.ROTATION_0)
}
}
}
- internal open val customRotateSetup: FlickerBuilder.(Bundle) -> Unit
- get() = { configuration ->
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview()
- secondaryApp.launchViaIntent(wmHelper)
- splitScreenApp.launchViaIntent(wmHelper)
- }
- }
- teardown {
- eachRun {
- splitScreenApp.exit()
- secondaryApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- }
+ 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 f2a7cda..1b4b54a 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
@@ -19,22 +19,24 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesInVisible
import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.buildTestTag
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.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -44,58 +46,73 @@
* (Non resizable activity launch via recent overview)
* To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreen`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class NonResizableDismissInLegacySplitScreen(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testNonResizableDismissInLegacySplitScreen", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ cleanSetup(this, configuration)
+ setup {
+ eachRun {
nonResizeableApp.launchViaIntent(wmHelper)
splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen()
- nonResizeableApp.reopenAppFromOverview()
- wmHelper.waitForAppTransitionIdle()
- }
- assertions {
- layersTrace {
- layerBecomesVisible(nonResizeableApp.defaultWindowName)
- layerBecomesInvisible(splitScreenApp.defaultWindowName)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
- appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName),
- bugId = 178447631
- )
- }
+ device.launchSplitScreen(wmHelper)
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, cleanSetup, testSpec,
+ transitions {
+ device.reopenAppFromOverview(wmHelper)
+ }
+ }
+
+ @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)
+ )
+
+ @Presubmit
+ @Test
+ fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesInVisible() =
+ testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+
+ @FlakyTest(bugId = 178447631)
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
+ LETTERBOX_NAME, TOAST_NAME,
+ splitScreenApp.defaultWindowName,
+ nonResizeableApp.defaultWindowName)
+ )
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
}
}
-}
\ No newline at end of file
+}
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 421ecff..2365e3b 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
@@ -19,15 +19,15 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesInVisible
import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.layerBecomesVisible
@@ -35,6 +35,7 @@
import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -47,56 +48,73 @@
@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class NonResizableLaunchInLegacySplitScreen(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testNonResizableLaunchInLegacySplitScreen", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ cleanSetup(this, configuration)
+ setup {
+ eachRun {
splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen()
- nonResizeableApp.launchViaIntent(wmHelper)
- wmHelper.waitForAppTransitionIdle()
- }
- assertions {
- layersTrace {
- layerBecomesVisible(nonResizeableApp.defaultWindowName)
- layerBecomesInvisible(splitScreenApp.defaultWindowName)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER,
- LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
- appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER,
- LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName),
- bugId = 178447631
- )
- }
+ device.launchSplitScreen(wmHelper)
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, cleanSetup, testSpec,
+ transitions {
+ nonResizeableApp.launchViaIntent(wmHelper)
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @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,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+ )
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesInVisible() =
+ testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+
+ @FlakyTest(bugId = 178447631)
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(DOCKED_STACK_DIVIDER,
+ LAUNCHER_PACKAGE_NAME,
+ LETTERBOX_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+ )
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
}
}
}
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 7edef93..b369a3d 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
@@ -19,14 +19,14 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.flicker.noUncoveredRegions
@@ -34,10 +34,10 @@
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
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -46,54 +46,66 @@
* Test open app to split screen.
* To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreen`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class OpenAppToLegacySplitScreen(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ transitions {
+ device.launchSplitScreen(wmHelper)
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
+
+ @FlakyTest(bugId = 178447631)
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName)
+ )
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.getPackage())
+
+ @FlakyTest
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun appPairsDividerBecomesVisible() = testSpec.appPairsDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun layerBecomesVisible() = testSpec.layerBecomesVisible(splitScreenApp.getPackage())
+
+ @FlakyTest(bugId = 178447631)
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName)
+ )
+
+ @FlakyTest(bugId = 151179149)
+ @Test
+ fun focusChanges() = testSpec.focusChanges(splitScreenApp.`package`,
+ "recents_animation_input_consumer", "NexusLauncherActivity")
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val wmHelper = WindowManagerStateHelper()
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testOpenAppToLegacySplitScreen", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- device.launchSplitScreen()
- wmHelper.waitForAppTransitionIdle()
- }
- assertions {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName),
- bugId = 178447631)
- appWindowBecomesVisible(splitScreenApp.getPackage())
- }
-
- layersTrace {
- noUncoveredRegions(configuration.startRotation, enabled = false)
- statusBarLayerIsAlwaysVisible()
- appPairsDividerBecomesVisible()
- layerBecomesVisible(splitScreenApp.getPackage())
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName),
- bugId = 178447631)
- }
-
- eventLog {
- focusChanges(splitScreenApp.`package`,
- "recents_animation_input_consumer", "NexusLauncherActivity",
- bugId = 151179149)
- }
- }
- }
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, defaultTransitionSetup, testSpec,
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
)
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 54a37d7..ffffa19 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
@@ -16,24 +16,21 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Presubmit
import android.graphics.Region
+import android.os.Bundle
import android.util.Rational
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isInSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.resizeSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
@@ -42,7 +39,6 @@
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
@@ -52,6 +48,7 @@
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -62,14 +59,162 @@
*
* Currently it runs only in 0 degrees because of b/156100803
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 159096424)
class ResizeLegacySplitScreen(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ private val testAppTop = SimpleAppHelper(instrumentation)
+ private val testAppBottom = ImeAppHelper(instrumentation)
+
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(configuration.startRotation)
+ this.launcherStrategy.clearRecentAppsFromOverview()
+ testAppBottom.launchViaIntent(wmHelper)
+ device.pressHome()
+ testAppTop.launchViaIntent(wmHelper)
+ device.waitForIdle()
+ device.launchSplitScreen(wmHelper)
+ val snapshot =
+ device.findObject(By.res(device.launcherPackageName, "snapshot"))
+ snapshot.click()
+ testAppBottom.openIME(device)
+ device.pressBack()
+ device.resizeSplitScreen(startRatio)
+ }
+ }
+ teardown {
+ eachRun {
+ testAppTop.exit(wmHelper)
+ testAppBottom.exit(wmHelper)
+ }
+ }
+ transitions {
+ device.resizeSplitScreen(stopRatio)
+ }
+ }
+
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 156223549)
+ @Test
+ fun topAppWindowIsAlwaysVisible() {
+ testSpec.assertWm {
+ this.showsAppWindow(sSimpleActivity)
+ }
+ }
+
+ @FlakyTest(bugId = 156223549)
+ @Test
+ fun bottomAppWindowIsAlwaysVisible() {
+ testSpec.assertWm {
+ this.showsAppWindow(sImeActivity)
+ }
+ }
+
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation)
+
+ @Test
+ fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation)
+
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Test
+ fun topAppLayerIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.showsLayer(sSimpleActivity)
+ }
+ }
+
+ @Test
+ fun bottomAppLayerIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.showsLayer(sImeActivity)
+ }
+ }
+
+ @Test
+ fun dividerLayerIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.showsLayer(DOCKED_STACK_DIVIDER)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun appsStartingBounds() {
+ testSpec.assertLayersStart {
+ val displayBounds = WindowUtils.displayBounds
+ val dividerBounds =
+ entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+
+ val topAppBounds = Region(0, 0, dividerBounds.right,
+ dividerBounds.top + WindowUtils.dockedStackDividerInset)
+ val bottomAppBounds = Region(0,
+ dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.right,
+ displayBounds.bottom - WindowUtils.navigationBarHeight)
+ this.hasVisibleRegion("SimpleActivity", topAppBounds)
+ .hasVisibleRegion("ImeActivity", bottomAppBounds)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun appsEndingBounds() {
+ testSpec.assertLayersStart {
+ val displayBounds = WindowUtils.displayBounds
+ val dividerBounds =
+ entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+
+ val topAppBounds = Region(0, 0, dividerBounds.right,
+ dividerBounds.top + WindowUtils.dockedStackDividerInset)
+ val bottomAppBounds = Region(0,
+ dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.right,
+ displayBounds.bottom - WindowUtils.navigationBarHeight)
+
+ this.hasVisibleRegion(sSimpleActivity, topAppBounds)
+ .hasVisibleRegion(sImeActivity, bottomAppBounds)
+ }
+ }
+
+ @Test
+ fun focusDoesNotChange() {
+ testSpec.assertEventLog {
+ focusDoesNotChange()
+ }
+ }
+
companion object {
private const val sSimpleActivity = "SimpleActivity"
private const val sImeActivity = "ImeActivity"
@@ -78,126 +223,14 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testAppTop = SimpleAppHelper(instrumentation)
- val testAppBottom = ImeAppHelper(instrumentation)
-
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
- withTestName {
- val description = (startRatio.toString().replace("/", "-") + "_to_" +
- stopRatio.toString().replace("/", "-"))
- buildTestTag("resizeSplitScreen", configuration, description)
- }
- repeat { configuration.repetitions }
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(configuration.startRotation)
- this.launcherStrategy.clearRecentAppsFromOverview()
- testAppBottom.launchViaIntent(wmHelper)
- device.pressHome()
- testAppTop.launchViaIntent(wmHelper)
- device.waitForIdle()
- device.launchSplitScreen()
- val snapshot =
- device.findObject(By.res(device.launcherPackageName, "snapshot"))
- snapshot.click()
- testAppBottom.openIME(device)
- device.pressBack()
- device.resizeSplitScreen(startRatio)
- }
- }
- teardown {
- eachRun {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- device.pressHome()
- testAppTop.exit()
- testAppBottom.exit()
- }
- test {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- }
- }
- transitions {
- device.resizeSplitScreen(stopRatio)
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- all("topAppWindowIsAlwaysVisible", bugId = 156223549) {
- this.showsAppWindow(sSimpleActivity)
- }
-
- all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) {
- this.showsAppWindow(sImeActivity)
- }
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.endRotation)
- navBarLayerRotatesAndScales(configuration.endRotation)
- statusBarLayerRotatesScales(configuration.endRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry()
-
- all("topAppLayerIsAlwaysVisible") {
- this.showsLayer(sSimpleActivity)
- }
-
- all("bottomAppLayerIsAlwaysVisible") {
- this.showsLayer(sImeActivity)
- }
-
- all("dividerLayerIsAlwaysVisible") {
- this.showsLayer(DOCKED_STACK_DIVIDER)
- }
-
- start("appsStartingBounds", enabled = false) {
- val displayBounds = WindowUtils.displayBounds
- val dividerBounds =
- entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
-
- val topAppBounds = Region(0, 0, dividerBounds.right,
- dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region(0,
- dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.right,
- displayBounds.bottom - WindowUtils.navigationBarHeight)
- this.hasVisibleRegion("SimpleActivity", topAppBounds)
- .hasVisibleRegion("ImeActivity", bottomAppBounds)
- }
-
- end("appsEndingBounds", enabled = false) {
- val displayBounds = WindowUtils.displayBounds
- val dividerBounds =
- entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
-
- val topAppBounds = Region(0, 0, dividerBounds.right,
- dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region(0,
- dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.right,
- displayBounds.bottom - WindowUtils.navigationBarHeight)
-
- this.hasVisibleRegion(sSimpleActivity, topAppBounds)
- .hasVisibleRegion(sImeActivity, bottomAppBounds)
- }
- }
-
- eventLog {
- focusDoesNotChange()
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0))
+ .map {
+ val description = (startRatio.toString().replace("/", "-") + "_to_" +
+ stopRatio.toString().replace("/", "-"))
+ val newName = "${FlickerTestParameter.defaultName(it.config)}_$description"
+ FlickerTestParameter(it.config, name = newName)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 214269e..c538008 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -19,14 +19,14 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -38,6 +38,7 @@
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -46,50 +47,64 @@
* Test dock activity to primary split screen and rotate
* To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppAndEnterSplitScreen`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class RotateOneLaunchedAppAndEnterSplitScreen(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenRotateTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ transitions {
+ device.launchSplitScreen(wmHelper)
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackPrimaryBoundsIsVisible() =
+ testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
+ splitScreenApp.defaultWindowName)
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
+ testSpec.config.endRotation)
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
+ testSpec.config.endRotation)
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testRotateOneLaunchedAppAndEnterSplitScreen", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- device.launchSplitScreen()
- this.setRotation(configuration.startRotation)
- }
- assertions {
- layersTrace {
- dockedStackDividerIsVisible(bugId = 175687842)
- dockedStackPrimaryBoundsIsVisible(
- configuration.startRotation,
- splitScreenApp.defaultWindowName, bugId = 175687842)
- navBarLayerRotatesAndScales(
- configuration.startRotation,
- configuration.endRotation, bugId = 169271943)
- statusBarLayerRotatesScales(
- configuration.startRotation,
- configuration.endRotation, bugId = 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- appWindowBecomesVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, customRotateSetup, testSpec,
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 4290c92..c116256 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -19,14 +19,14 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -38,6 +38,7 @@
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -46,50 +47,61 @@
* Rotate
* To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppInSplitScreenMode`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class RotateOneLaunchedAppInSplitScreenMode(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenRotateTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ transitions {
+ this.setRotation(testSpec.config.startRotation)
+ device.launchSplitScreen(wmHelper)
+ }
+ }
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackPrimaryBoundsIsVisible() = testSpec.dockedStackPrimaryBoundsIsVisible(
+ testSpec.config.startRotation, splitScreenApp.defaultWindowName)
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testRotateOneLaunchedAppInSplitScreenMode", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- this.setRotation(configuration.startRotation)
- device.launchSplitScreen()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsVisible(bugId = 175687842)
- dockedStackPrimaryBoundsIsVisible(
- configuration.startRotation,
- splitScreenApp.defaultWindowName, bugId = 175687842)
- navBarLayerRotatesAndScales(
- configuration.startRotation,
- configuration.endRotation, bugId = 169271943)
- statusBarLayerRotatesScales(
- configuration.startRotation,
- configuration.endRotation, bugId = 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- appWindowBecomesVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, customRotateSetup, testSpec,
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 4095b9a..273925c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -19,15 +19,16 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -39,6 +40,7 @@
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -47,54 +49,69 @@
* Test open app to split screen.
* To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppAndEnterSplitScreen`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class RotateTwoLaunchedAppAndEnterSplitScreen(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenRotateTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ transitions {
+ this.setRotation(testSpec.config.startRotation)
+ device.launchSplitScreen(wmHelper)
+ device.reopenAppFromOverview(wmHelper)
+ }
+ }
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackPrimaryBoundsIsVisible() =
+ testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
+ splitScreenApp.defaultWindowName)
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackSecondaryBoundsIsVisible() =
+ testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
+ secondaryApp.defaultWindowName)
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
+ testSpec.config.endRotation)
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testRotateTwoLaunchedAppAndEnterSplitScreen", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- this.setRotation(configuration.startRotation)
- device.launchSplitScreen()
- secondaryApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsVisible(bugId = 175687842)
- dockedStackPrimaryBoundsIsVisible(
- configuration.startRotation,
- splitScreenApp.defaultWindowName, 175687842)
- dockedStackSecondaryBoundsIsVisible(
- configuration.startRotation,
- secondaryApp.defaultWindowName, bugId = 175687842)
- navBarLayerRotatesAndScales(
- configuration.startRotation,
- configuration.endRotation, bugId = 169271943)
- statusBarLayerRotatesScales(
- configuration.startRotation,
- configuration.endRotation, bugId = 169271943)
- }
- windowManagerTrace {
- appWindowBecomesVisible(secondaryApp.defaultWindowName)
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- }
- }
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, customRotateSetup, testSpec,
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index aebf606..c7188dc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -19,15 +19,16 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -39,6 +40,7 @@
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -47,59 +49,76 @@
* Test open app to split screen.
* To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppInSplitScreenMode`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class RotateTwoLaunchedAppInSplitScreenMode(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- buildTestTag("testRotateTwoLaunchedAppInSplitScreenMode", configuration)
- }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- setup {
- eachRun {
- device.launchSplitScreen()
- splitScreenApp.reopenAppFromOverview()
- this.setRotation(configuration.startRotation)
- }
- }
- transitions {
- this.setRotation(configuration.startRotation)
- }
- assertions {
- layersTrace {
- dockedStackDividerIsVisible(bugId = 175687842)
- dockedStackPrimaryBoundsIsVisible(
- configuration.startRotation,
- splitScreenApp.defaultWindowName, bugId = 175687842)
- dockedStackSecondaryBoundsIsVisible(
- configuration.startRotation,
- secondaryApp.defaultWindowName, bugId = 175687842)
- navBarLayerRotatesAndScales(
- configuration.startRotation,
- configuration.endRotation, bugId = 169271943)
- statusBarLayerRotatesScales(
- configuration.startRotation,
- configuration.endRotation, bugId = 169271943)
- }
- windowManagerTrace {
- appWindowBecomesVisible(secondaryApp.defaultWindowName)
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenRotateTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ super.transition(this, configuration)
+ setup {
+ eachRun {
+ device.launchSplitScreen(wmHelper)
+ device.reopenAppFromOverview(wmHelper)
+ this.setRotation(testSpec.config.startRotation)
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(
- instrumentation, customRotateSetup, testSpec,
+ transitions {
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackPrimaryBoundsIsVisible() =
+ testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
+ splitScreenApp.defaultWindowName)
+
+ @FlakyTest(bugId = 175687842)
+ @Test
+ fun dockedStackSecondaryBoundsIsVisible() =
+ testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
+ secondaryApp.defaultWindowName)
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
+ testSpec.config.endRotation)
+
+ @FlakyTest(bugId = 169271943)
+ @Test
+ fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
+ testSpec.config.endRotation)
+
+ @Presubmit
+ @Test
+ fun appWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
deleted file mode 100644
index bc42d5e..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
+++ /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.wm.shell.flicker.pip
-
-import android.os.SystemClock
-import com.android.wm.shell.flicker.NonRotationTestBase
-
-abstract class AppTestBase(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- companion object {
- fun waitForAnimationComplete() {
- // TODO: UiDevice doesn't have reliable way to wait for the completion of animation.
- // Consider to introduce WindowManagerStateHelper to access Activity state.
- SystemClock.sleep(1000)
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index d56ed02..ca48eaa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -16,11 +16,14 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -29,6 +32,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -39,64 +43,96 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EnterExitPipTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<Array<Any>> {
- val testApp = FixedAppHelper(instrumentation)
- val testSpec = getTransition(eachRun = true) { configuration ->
- setup {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- }
- }
- transitions {
- // This will bring PipApp to fullscreen
- pipApp.launchViaIntent(wmHelper)
- }
- assertions {
- val displayBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
- presubmit {
- windowManagerTrace {
- all("pipApp must remain inside visible bounds") {
- coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
- }
- all("Initially shows both app windows then pipApp hides testApp") {
- showsAppWindow(testApp.defaultWindowName)
- .showsAppWindowOnTop(pipApp.defaultWindowName)
- .then()
- .hidesAppWindow(testApp.defaultWindowName)
- }
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- layersTrace {
- all("Initially shows both app layers then pipApp hides testApp") {
- showsLayer(testApp.defaultWindowName)
- .showsLayer(pipApp.defaultWindowName)
- .then()
- .hidesLayer(testApp.defaultWindowName)
- }
- start("testApp covers the fullscreen, pipApp remains inside display") {
- hasVisibleRegion(testApp.defaultWindowName, displayBounds)
- coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
- }
- end("pipApp covers the fullscreen") {
- hasVisibleRegion(pipApp.defaultWindowName, displayBounds)
- }
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- }
- }
+ testSpec: FlickerTestParameter
+) : PipTransition(testSpec) {
+ private val testApp = FixedAppHelper(instrumentation)
+ private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = buildTransition(eachRun = true) {
+ setup {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- testSpec, supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 5)
+ transitions {
+ // This will bring PipApp to fullscreen
+ pipApp.launchViaIntent(wmHelper)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun pipAppRemainInsideVisibleBounds() {
+ testSpec.assertWm {
+ coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
}
}
-}
\ No newline at end of file
+
+ @Presubmit
+ @Test
+ fun showBothAppWindowsThenHidePip() {
+ testSpec.assertWm {
+ showsAppWindow(testApp.defaultWindowName)
+ .showsAppWindowOnTop(pipApp.defaultWindowName)
+ .then()
+ .hidesAppWindow(testApp.defaultWindowName)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun showBothAppLayersThenHidePip() {
+ testSpec.assertLayers {
+ showsLayer(testApp.defaultWindowName)
+ .showsLayer(pipApp.defaultWindowName)
+ .then()
+ .hidesLayer(testApp.defaultWindowName)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun testAppCoversFullScreenWithPipOnDisplay() {
+ testSpec.assertLayersStart {
+ hasVisibleRegion(testApp.defaultWindowName, displayBounds)
+ coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun pipAppCoversFullScreen() {
+ testSpec.assertLayersEnd {
+ hasVisibleRegion(pipApp.defaultWindowName, displayBounds)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index ff31ba7..e1fbc16 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,11 +16,15 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
@@ -30,6 +34,7 @@
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -40,58 +45,71 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterPipTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<Array<Any>> {
- val testSpec = getTransition(eachRun = true,
- stringExtras = emptyMap()) { configuration ->
- transitions {
- pipApp.clickEnterPipButton()
- pipApp.expandPipWindow(wmHelper)
- }
- assertions {
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
-
- all("pipWindowBecomesVisible") {
- this.showsAppWindow(pipApp.defaultWindowName)
- }
- }
-
- layersTrace {
- statusBarLayerIsAlwaysVisible()
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
- }
-
- layersTrace {
- all("pipLayerBecomesVisible") {
- this.showsLayer(pipApp.launcherName)
- }
- }
- }
-
- flaky {
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, bugId = 140855415)
- }
- }
- }
+class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
+ transitions {
+ pipApp.clickEnterPipButton()
+ pipApp.expandPipWindow(wmHelper)
}
+ }
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- testSpec, supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 5)
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun pipWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.showsAppWindow(pipApp.defaultWindowName)
}
}
-}
\ No newline at end of file
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ fun pipLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.showsLayer(pipApp.launcherName)
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun noUncoveredRegions() =
+ testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index eaaa2f6..215b97b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -16,22 +16,27 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
-import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -42,82 +47,108 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EnterPipToOtherOrientationTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
- private val testApp = FixedAppHelper(instrumentation)
+ testSpec: FlickerTestParameter
+) : PipTransition(testSpec) {
+ private val testApp = FixedAppHelper(instrumentation)
+ private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
+ private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 5) { configuration ->
- setupAndTeardown(this, configuration)
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ setupAndTeardown(this, configuration)
- setup {
- eachRun {
- // Launch a portrait only app on the fullscreen stack
- testApp.launchViaIntent(wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()))
- // Launch the PiP activity fixed as landscape
- pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
- }
- }
- teardown {
- eachRun {
- pipApp.exit()
- testApp.exit()
- }
- }
- transitions {
- // Enter PiP, and assert that the PiP is within bounds now that the device is back
- // in portrait
- broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
- wmHelper.waitPipWindowShown()
- wmHelper.waitForAppTransitionIdle()
- }
- assertions {
- val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
-
- presubmit {
- windowManagerTrace {
- all("pipApp window is always on top") {
- showsAppWindowOnTop(pipApp.defaultWindowName)
- }
- start("pipApp window hides testApp") {
- isInvisible(testApp.defaultWindowName)
- }
- end("testApp windows is shown") {
- isVisible(testApp.defaultWindowName)
- }
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
-
- layersTrace {
- start("pipApp layer hides testApp") {
- hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
- isInvisible(testApp.defaultWindowName)
- }
- }
- }
-
- flaky {
- layersTrace {
- end("testApp layer covers fullscreen") {
- hasVisibleRegion(testApp.defaultWindowName, endingBounds)
- }
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- }
- }
+ setup {
+ eachRun {
+ // Launch a portrait only app on the fullscreen stack
+ testApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()))
+ // Launch the PiP activity fixed as landscape
+ pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
}
}
+ teardown {
+ eachRun {
+ pipApp.exit(wmHelper)
+ testApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ // Enter PiP, and assert that the PiP is within bounds now that the device is back
+ // in portrait
+ broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
+ wmHelper.waitPipWindowShown()
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun pipAppWindowIsAlwaysOnTop() {
+ testSpec.assertWm {
+ showsAppWindowOnTop(pipApp.defaultWindowName)
}
}
-}
\ No newline at end of file
+
+ @Presubmit
+ @Test
+ fun pipAppHidesTestApp() {
+ testSpec.assertWmStart {
+ isInvisible(testApp.defaultWindowName)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun testAppWindowIsVisible() {
+ testSpec.assertWmEnd {
+ isVisible(testApp.defaultWindowName)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun pipAppLayerHidesTestApp() {
+ testSpec.assertLayersStart {
+ hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
+ isInvisible(testApp.defaultWindowName)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun testAppLayerCoversFullScreen() {
+ testSpec.assertLayersEnd {
+ hasVisibleRegion(testApp.defaultWindowName, endingBounds)
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index f054e64..f3b9ea1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -16,17 +16,21 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
import com.android.wm.shell.flicker.IME_WINDOW_NAME
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -37,58 +41,67 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) {
- companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+ private val imeApp = ImeAppHelper(instrumentation)
+
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = buildTransition(eachRun = false) { configuration ->
+ setup {
+ test {
+ imeApp.launchViaIntent(wmHelper)
+ setRotation(configuration.startRotation)
+ }
+ }
+ teardown {
+ test {
+ imeApp.exit(wmHelper)
+ setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ // open the soft keyboard
+ imeApp.openIME(wmHelper)
+ createTag(TAG_IME_VISIBLE)
+
+ // then close it again
+ imeApp.closeIME(wmHelper)
+ }
+ }
+
+ /**
+ * Ensure the pip window remains visible throughout any keyboard interactions
+ */
+ @Presubmit
+ @Test
+ fun pipInVisibleBounds() {
+ testSpec.assertWm {
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+ }
+ }
+
+ /**
+ * Ensure that the pip window does not obscure the keyboard
+ */
+ @Presubmit
+ @Test
+ fun pipIsAboveAppWindow() {
+ testSpec.assertWmTag(TAG_IME_VISIBLE) {
+ isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName)
+ }
+ }
+
+ companion object {
private const val TAG_IME_VISIBLE = "imeIsVisible"
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val imeApp = ImeAppHelper(instrumentation)
- val testSpec = getTransition(eachRun = false) { configuration ->
- setup {
- test {
- imeApp.launchViaIntent(wmHelper)
- setRotation(configuration.startRotation)
- }
- }
- teardown {
- test {
- imeApp.exit()
- setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- // open the soft keyboard
- imeApp.openIME(wmHelper)
- createTag(TAG_IME_VISIBLE)
-
- // then close it again
- imeApp.closeIME(wmHelper)
- }
- assertions {
- presubmit {
- windowManagerTrace {
- // Ensure the pip window remains visible throughout
- // any keyboard interactions
- all("pipInVisibleBounds") {
- val displayBounds = WindowUtils.getDisplayBounds(
- configuration.startRotation)
- coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
- }
- // Ensure that the pip window does not obscure the keyboard
- tag(TAG_IME_VISIBLE) {
- isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- testSpec, supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 5)
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index f10bd7f..daf381e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -16,21 +16,25 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
+import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runFlicker
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isInSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+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.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.removeAllTasksButHome
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
@@ -46,83 +50,103 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 161435597)
-class PipLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : AppTestBase(rotationName, rotation) {
- private val pipApp = PipAppHelper(instrumentation)
+class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
private val testApp = FixedAppHelper(instrumentation)
+ private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- @Test
- fun testShowsPipLaunchingToSplitScreen() {
- runFlicker(instrumentation) {
- withTestName { "testShowsPipLaunchingToSplitScreen" }
- repeat { TEST_REPETITIONS }
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
setup {
test {
removeAllTasksButHome()
device.wakeUpAndGoToHomeScreen()
- pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true"))
- waitForAnimationComplete()
+ pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true"),
+ wmHelper = wmHelper)
}
}
transitions {
- testApp.launchViaIntent()
- device.launchSplitScreen()
- imeApp.launchViaIntent()
- waitForAnimationComplete()
+ testApp.launchViaIntent(wmHelper)
+ device.launchSplitScreen(wmHelper)
+ imeApp.launchViaIntent(wmHelper)
}
teardown {
eachRun {
- imeApp.exit()
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- testApp.exit()
+ imeApp.exit(wmHelper)
+ testApp.exit(wmHelper)
}
test {
removeAllTasksButHome()
}
}
- assertions {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- windowManagerTrace {
- all("PIP window must remain inside visible bounds") {
- coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
- }
- end("Both app windows should be visible") {
- isVisible(testApp.defaultWindowName)
- isVisible(imeApp.defaultWindowName)
- noWindowsOverlap(setOf(testApp.defaultWindowName, imeApp.defaultWindowName))
- }
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- layersTrace {
- all("PIP layer must remain inside visible bounds") {
- coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
- }
- end("Both app layers should be visible") {
- coversAtMostRegion(displayBounds, testApp.defaultWindowName)
- coversAtMostRegion(displayBounds, imeApp.defaultWindowName)
- }
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- }
- }
+ }
+
+ @Postsubmit
+ @Test
+ fun pipWindowInsideDisplayBounds() {
+ testSpec.assertWm {
+ coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
}
}
- companion object {
- const val TEST_REPETITIONS = 2
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ @Postsubmit
+ @Test
+ fun bothAppWindowsVisible() {
+ testSpec.assertWmEnd {
+ isVisible(testApp.defaultWindowName)
+ isVisible(imeApp.defaultWindowName)
+ noWindowsOverlap(setOf(testApp.defaultWindowName, imeApp.defaultWindowName))
}
}
-}
\ No newline at end of file
+
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun pipLayerInsideDisplayBounds() {
+ testSpec.assertLayers {
+ coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun bothAppLayersVisible() {
+ testSpec.assertLayersEnd {
+ coversAtMostRegion(displayBounds, testApp.defaultWindowName)
+ coversAtMostRegion(displayBounds, imeApp.defaultWindowName)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ companion object {
+ const val TEST_REPETITIONS = 2
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = TEST_REPETITIONS
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index ade65ac..43c12ac 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -16,11 +16,15 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
@@ -34,6 +38,7 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -44,73 +49,91 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipRotationTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val fixedApp = FixedAppHelper(instrumentation)
- val testSpec = getTransition(eachRun = false) { configuration ->
- setup {
- test {
- fixedApp.launchViaIntent(wmHelper)
- }
- eachRun {
- setRotation(configuration.startRotation)
- }
+class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+ private val fixedApp = FixedAppHelper(instrumentation)
+ private val startingBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ private val endingBounds = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
+
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = buildTransition(eachRun = false) { configuration ->
+ setup {
+ test {
+ fixedApp.launchViaIntent(wmHelper)
}
- transitions {
- setRotation(configuration.endRotation)
- }
- teardown {
- eachRun {
- setRotation(Surface.ROTATION_0)
- }
- }
- assertions {
- val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
- val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation)
-
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
-
- layersTrace {
- noUncoveredRegions(configuration.startRotation,
- configuration.endRotation, allStates = false)
- }
- }
-
- flaky {
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- navBarLayerRotatesAndScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
- statusBarLayerRotatesScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
-
- start("appLayerRotates_StartingBounds", bugId = 140855415) {
- hasVisibleRegion(fixedApp.defaultWindowName, startingBounds)
- coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
- }
- end("appLayerRotates_EndingBounds", bugId = 140855415) {
- hasVisibleRegion(fixedApp.defaultWindowName, endingBounds)
- coversAtMostRegion(endingBounds, pipApp.defaultWindowName)
- }
- }
- }
+ eachRun {
+ setRotation(configuration.startRotation)
}
}
+ transitions {
+ setRotation(configuration.endRotation)
+ }
+ teardown {
+ eachRun {
+ setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
- return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation,
- testSpec, supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ testSpec.config.endRotation, allStates = false)
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
+ testSpec.config.endRotation)
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
+ testSpec.config.endRotation)
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun appLayerRotates_StartingBounds() {
+ testSpec.assertLayersStart {
+ hasVisibleRegion(fixedApp.defaultWindowName, startingBounds)
+ coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun appLayerRotates_EndingBounds() {
+ testSpec.assertLayersEnd {
+ hasVisibleRegion(fixedApp.defaultWindowName, endingBounds)
+ coversAtMostRegion(endingBounds, pipApp.defaultWindowName)
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigRotationTests(
+ supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
repetitions = 5)
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
index 96b6c91..7ba085d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
@@ -16,13 +16,14 @@
package com.android.wm.shell.flicker.pip
+import com.android.wm.shell.flicker.FlickerTestBase
import com.android.wm.shell.flicker.helpers.PipAppHelper
import org.junit.Before
abstract class PipTestBase(
rotationName: String,
rotation: Int
-) : AppTestBase(rotationName, rotation) {
+) : FlickerTestBase(rotationName, rotation) {
protected val testApp = PipAppHelper(instrumentation)
@Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index f2d5899..02389a9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -16,11 +16,15 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -32,6 +36,7 @@
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -42,73 +47,89 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipToAppTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<Array<Any>> {
- val testSpec = getTransition(eachRun = true) { configuration ->
- setup {
- eachRun {
- this.setRotation(configuration.startRotation)
- }
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- pipApp.expandPipWindowToApp(wmHelper)
- }
- assertions {
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
-
- all("appReplacesPipWindow") {
- this.showsAppWindow(PIP_WINDOW_TITLE)
- .then()
- .showsAppWindowOnTop(pipApp.launcherName)
- }
- }
-
- layersTrace {
- statusBarLayerIsAlwaysVisible()
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
-
- all("appReplacesPipLayer") {
- this.showsLayer(PIP_WINDOW_TITLE)
- .then()
- .showsLayer(pipApp.launcherName)
- }
- }
- }
-
- flaky {
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, bugId = 140855415)
- }
-
- eventLog {
- focusChanges(
- "NexusLauncherActivity", pipApp.launcherName,
- "NexusLauncherActivity", bugId = 151179149)
- }
- }
+class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = buildTransition(eachRun = true) { configuration ->
+ setup {
+ eachRun {
+ this.setRotation(configuration.startRotation)
}
}
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ pipApp.expandPipWindowToApp(wmHelper)
+ }
+ }
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun appReplacesPipWindow() {
+ testSpec.assertWm {
+ this.showsAppWindow(PIP_WINDOW_TITLE)
+ .then()
+ .showsAppWindowOnTop(pipApp.launcherName)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ fun appReplacesPipLayer() {
+ testSpec.assertLayers {
+ this.showsLayer(PIP_WINDOW_TITLE)
+ .then()
+ .showsLayer(pipApp.launcherName)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun noUncoveredRegions() =
+ testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @FlakyTest(bugId = 151179149)
+ @Test
+ fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity",
+ pipApp.launcherName, "NexusLauncherActivity")
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
index 1b44377..968a11d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
@@ -16,15 +16,19 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
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.startRotation
@@ -32,6 +36,7 @@
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -42,74 +47,88 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipToHomeTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<Array<Any>> {
- val testSpec = getTransition(eachRun = true) { configuration ->
- setup {
- eachRun {
- this.setRotation(configuration.startRotation)
- }
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- pipApp.closePipWindow(wmHelper)
- }
- assertions {
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
-
- all("pipWindowBecomesInvisible") {
- this.showsAppWindow(PIP_WINDOW_TITLE)
- .then()
- .hidesAppWindow(PIP_WINDOW_TITLE)
- }
- }
-
- layersTrace {
- statusBarLayerIsAlwaysVisible()
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
-
- all("pipLayerBecomesInvisible") {
- this.showsLayer(PIP_WINDOW_TITLE)
- .then()
- .hidesLayer(PIP_WINDOW_TITLE)
- }
- }
- }
-
- postsubmit {
- layersTrace {
- navBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0)
- }
- }
-
- flaky {
- eventLog {
- focusChanges(pipApp.launcherName, "NexusLauncherActivity",
- bugId = 151179149)
- }
- }
+class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = buildTransition(eachRun = true) { configuration ->
+ setup {
+ eachRun {
+ this.setRotation(configuration.startRotation)
}
}
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ pipApp.closePipWindow(wmHelper)
+ }
+ }
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
+ @Postsubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun pipWindowBecomesInvisible() {
+ testSpec.assertWm {
+ this.showsAppWindow(PIP_WINDOW_TITLE)
+ .then()
+ .hidesAppWindow(PIP_WINDOW_TITLE)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun pipLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.showsLayer(PIP_WINDOW_TITLE)
+ .then()
+ .hidesLayer(PIP_WINDOW_TITLE)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @Postsubmit
+ @Test
+ fun noUncoveredRegions() =
+ testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @Postsubmit
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @FlakyTest(bugId = 151179149)
+ @Test
+ fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
similarity index 80%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b1e404e..a94483e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -20,16 +20,26 @@
import android.content.Intent
import android.os.Bundle
import android.view.Surface
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.isRotated
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.wm.shell.flicker.helpers.PipAppHelper
import com.android.wm.shell.flicker.removeAllTasksButHome
import com.android.wm.shell.flicker.testapp.Components
-abstract class PipTransitionBase(protected val instrumentation: Instrumentation) {
+abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val isRotated = testSpec.config.startRotation.isRotated()
+ protected val pipApp = PipAppHelper(instrumentation)
+ protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
+ protected abstract val transition: FlickerBuilder.(Bundle) -> Unit
+
// Helper class to process test actions by broadcast.
protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
private fun createIntentWithAction(broadcastAction: String): Intent {
@@ -59,16 +69,20 @@
}
}
- protected val pipApp = PipAppHelper(instrumentation)
- protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ transition(this, testSpec.config)
+ }
+ }
/**
* Gets a configuration that handles basic setup and teardown of pip tests
*/
protected val setupAndTeardown: FlickerBuilder.(Bundle) -> Unit
- get() = { configuration ->
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
+ get() = {
setup {
test {
removeAllTasksButHome()
@@ -81,7 +95,7 @@
}
test {
removeAllTasksButHome()
- pipApp.exit()
+ pipApp.exit(wmHelper)
}
}
}
@@ -95,7 +109,7 @@
* @param extraSpec Addicional segment of flicker specification
*/
@JvmOverloads
- open fun getTransition(
+ protected open fun buildTransition(
eachRun: Boolean,
stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"),
extraSpec: FlickerBuilder.(Bundle) -> Unit = {}
@@ -121,12 +135,12 @@
teardown {
eachRun {
if (eachRun) {
- pipApp.exit()
+ pipApp.exit(wmHelper)
}
}
test {
if (!eachRun) {
- pipApp.exit()
+ pipApp.exit(wmHelper)
}
removeAllTasksButHome()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index c01bc94..1f0370d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,21 +16,26 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.Assert.assertEquals
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -41,75 +46,99 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class SetRequestedOrientationWhilePinnedTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 1) { configuration ->
- setupAndTeardown(this, configuration)
+ testSpec: FlickerTestParameter
+) : PipTransition(testSpec) {
+ private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
+ private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- setup {
- eachRun {
- // Launch the PiP activity fixed as landscape
- pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(),
- EXTRA_ENTER_PIP to "true"))
- }
- }
- teardown {
- eachRun {
- pipApp.exit()
- }
- }
- transitions {
- // Request that the orientation is set to landscape
- broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE)
+ override val transition: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ setupAndTeardown(this, configuration)
- // Launch the activity back into fullscreen and
- // ensure that it is now in landscape
- pipApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(pipApp.component)
- wmHelper.waitForRotation(Surface.ROTATION_90)
- assertEquals(Surface.ROTATION_90, device.displayRotation)
- }
- assertions {
- val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
- val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- presubmit {
- windowManagerTrace {
- start("PIP window must remain inside display") {
- coversAtMostRegion(pipApp.defaultWindowName, startingBounds)
- }
- end("pipApp shows on top") {
- showsAppWindowOnTop(pipApp.defaultWindowName)
- }
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- layersTrace {
- start("PIP layer must remain inside display") {
- coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
- }
- end("pipApp layer covers fullscreen") {
- hasVisibleRegion(pipApp.defaultWindowName, endingBounds)
- }
- }
- }
-
- flaky {
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- }
- }
+ setup {
+ eachRun {
+ // Launch the PiP activity fixed as landscape
+ pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(),
+ EXTRA_ENTER_PIP to "true"))
}
}
+ teardown {
+ eachRun {
+ pipApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ // Request that the orientation is set to landscape
+ broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE)
+
+ // Launch the activity back into fullscreen and
+ // ensure that it is now in landscape
+ pipApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(pipApp.component)
+ wmHelper.waitForRotation(Surface.ROTATION_90)
+ assertEquals(Surface.ROTATION_90, device.displayRotation)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun pipWindowInsideDisplay() {
+ testSpec.assertWmStart {
+ coversAtMostRegion(pipApp.defaultWindowName, startingBounds)
}
}
-}
\ No newline at end of file
+
+ @Presubmit
+ @Test
+ fun pipAppShowsOnTop() {
+ testSpec.assertWmEnd {
+ showsAppWindowOnTop(pipApp.defaultWindowName)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun pipLayerInsideDisplay() {
+ testSpec.assertLayersStart {
+ coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun pipAppLayerCoversFullScreen() {
+ testSpec.assertLayersEnd {
+ hasVisibleRegion(pipApp.defaultWindowName, endingBounds)
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 1)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
index 26627a4..ea606df 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "WMShellFlickerTestApp",
srcs: ["**/*.java"],
@@ -23,4 +32,4 @@
name: "wmshell-flicker-test-components",
srcs: ["src/**/Components.java"],
sdk_version: "test_current",
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
index 4160280..bdf75fc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles.storage
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
@@ -31,9 +32,10 @@
class BubblePersistentRepositoryTest : ShellTestCase() {
private val bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0),
- BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title"),
- BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0, null, 1),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title", 2),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0, null,
+ INVALID_TASK_ID)
)
private lateinit var repository: BubblePersistentRepository
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index dd1a6a5..05795fd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles.storage
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.content.pm.LauncherApps
import android.os.UserHandle
import android.testing.AndroidTestingRunner
@@ -37,10 +38,12 @@
private val user0 = UserHandle.of(0)
private val user10 = UserHandle.of(10)
- private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0)
+ private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0,
+ null, 1)
private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob",
- "key-2", 0, 16537428, "title")
- private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
+ "key-2", 0, 16537428, "title", 2)
+ private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0,
+ null, INVALID_TASK_ID)
private val bubbles = listOf(bubble1, bubble2, bubble3)
@@ -105,13 +108,13 @@
@Test
fun testAddBubbleMatchesByKey() {
- val bubble = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, "title")
+ val bubble = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, "title", 1)
repository.addBubbles(listOf(bubble))
assertEquals(bubble, repository.bubbles.get(0))
// Same key as first bubble but different entry
val bubbleModified = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0,
- "different title")
+ "different title", 2)
repository.addBubbles(listOf(bubbleModified))
assertEquals(bubbleModified, repository.bubbles.get(0))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
index e0891a9..839b873 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles.storage
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
@@ -31,17 +32,18 @@
class BubbleXmlHelperTest : ShellTestCase() {
private val bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0),
- BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title"),
- BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0)
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0, null, 1),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title", 2),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0, null,
+ INVALID_TASK_ID)
)
@Test
fun testWriteXml() {
val expectedEntries = """
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" tid="1" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" tid="2" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" tid="-1" />
""".trimIndent()
ByteArrayOutputStream().use {
writeXml(it, bubbles)
@@ -56,9 +58,9 @@
val src = """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<bs v="1">
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" tid="1" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" tid="2" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" tid="-1" />
</bs>
""".trimIndent()
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
@@ -79,4 +81,32 @@
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
assertEquals("failed parsing bubbles from xml\n$src", emptyList<BubbleEntity>(), actual)
}
+
+ /**
+ * In S we changed the XML to include a taskId, version didn't increase because we can set a
+ * reasonable default for taskId (INVALID_TASK_ID) if it wasn't in the XML previously, this
+ * tests that that works.
+ */
+ @Test
+ fun testReadXMLWithoutTaskId() {
+ val expectedBubbles = listOf(
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0, null,
+ INVALID_TASK_ID),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title",
+ INVALID_TASK_ID),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0, null,
+ INVALID_TASK_ID)
+ )
+
+ val src = """
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<bs v="1">
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+</bs>
+ """.trimIndent()
+ val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
+ assertEquals("failed parsing bubbles from xml\n$src", expectedBubbles, actual)
+ }
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
new file mode 100644
index 0000000..9845d46
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.sizecompatui;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.widget.Button;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link SizeCompatHintPopup}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:SizeCompatHintPopupTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SizeCompatHintPopupTest extends ShellTestCase {
+
+ @Mock private SyncTransactionQueue mSyncTransactionQueue;
+ @Mock private IBinder mActivityToken;
+ @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+ @Mock private DisplayLayout mDisplayLayout;
+
+ private SizeCompatUILayout mLayout;
+ private SizeCompatHintPopup mHint;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final int taskId = 1;
+ mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mContext, new Configuration(),
+ taskId, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/);
+ mHint = (SizeCompatHintPopup)
+ LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
+ mHint.inject(mLayout);
+
+ spyOn(mLayout);
+ }
+
+ @Test
+ public void testOnClick() {
+ doNothing().when(mLayout).dismissHint();
+
+ final Button button = mHint.findViewById(R.id.got_it);
+ button.performClick();
+
+ verify(mLayout).dismissHint();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
index d9086a6..5a43925 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
@@ -19,13 +19,13 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
import android.os.IBinder;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
+import android.widget.ImageButton;
import androidx.test.filters.SmallTest;
@@ -71,45 +71,25 @@
mButton.inject(mLayout);
spyOn(mLayout);
- spyOn(mButton);
- doNothing().when(mButton).showHint();
}
@Test
public void testOnClick() {
doNothing().when(mLayout).onRestartButtonClicked();
- mButton.onClick(mButton);
+ final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
+ button.performClick();
verify(mLayout).onRestartButtonClicked();
}
@Test
public void testOnLongClick() {
- verify(mButton, never()).showHint();
+ doNothing().when(mLayout).onRestartButtonLongClicked();
- mButton.onLongClick(mButton);
+ final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
+ button.performLongClick();
- verify(mButton).showHint();
- }
-
- @Test
- public void testOnAttachedToWindow_showHint() {
- mLayout.mShouldShowHint = false;
- mButton.onAttachedToWindow();
-
- verify(mButton, never()).showHint();
-
- mLayout.mShouldShowHint = true;
- mButton.onAttachedToWindow();
-
- verify(mButton).showHint();
- }
-
- @Test
- public void testRemove() {
- mButton.remove();
-
- verify(mButton).dismissHint();
+ verify(mLayout).onRestartButtonLongClicked();
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
index 236db44..f33cfe8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
@@ -18,6 +18,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -27,6 +28,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityClient;
@@ -68,6 +70,7 @@
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private DisplayLayout mDisplayLayout;
@Mock private SizeCompatRestartButton mButton;
+ @Mock private SizeCompatHintPopup mHint;
private Configuration mTaskConfig;
private SizeCompatUILayout mLayout;
@@ -81,8 +84,13 @@
TASK_ID, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/);
spyOn(mLayout);
- spyOn(mLayout.mWindowManager);
- doReturn(mButton).when(mLayout.mWindowManager).createSizeCompatUI();
+ spyOn(mLayout.mButtonWindowManager);
+ doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton();
+
+ final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager();
+ spyOn(hintWindowManager);
+ doReturn(mHint).when(hintWindowManager).createSizeCompatHint();
+ doReturn(hintWindowManager).when(mLayout).createHintWindowManager();
}
@Test
@@ -90,24 +98,45 @@
// Not create button if IME is showing.
mLayout.createSizeCompatButton(true /* isImeShowing */);
- verify(mLayout.mWindowManager, never()).createSizeCompatUI();
+ verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton();
assertNull(mLayout.mButton);
+ assertNull(mLayout.mHintWindowManager);
+ assertNull(mLayout.mHint);
+ // Not create hint popup.
+ mLayout.mShouldShowHint = false;
mLayout.createSizeCompatButton(false /* isImeShowing */);
- verify(mLayout.mWindowManager).createSizeCompatUI();
+ verify(mLayout.mButtonWindowManager).createSizeCompatButton();
assertNotNull(mLayout.mButton);
+ assertNull(mLayout.mHintWindowManager);
+ assertNull(mLayout.mHint);
+
+ // Create hint popup.
+ mLayout.release();
+ mLayout.mShouldShowHint = true;
+ mLayout.createSizeCompatButton(false /* isImeShowing */);
+
+ verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton();
+ assertNotNull(mLayout.mButton);
+ assertNotNull(mLayout.mHintWindowManager);
+ verify(mLayout.mHintWindowManager).createSizeCompatHint();
+ assertNotNull(mLayout.mHint);
+ assertFalse(mLayout.mShouldShowHint);
}
@Test
public void testRelease() {
mLayout.createSizeCompatButton(false /* isImeShowing */);
+ final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
mLayout.release();
assertNull(mLayout.mButton);
- verify(mButton).remove();
- verify(mLayout.mWindowManager).release();
+ assertNull(mLayout.mHint);
+ verify(hintWindowManager).release();
+ assertNull(mLayout.mHintWindowManager);
+ verify(mLayout.mButtonWindowManager).release();
}
@Test
@@ -119,7 +148,7 @@
mLayout.updateSizeCompatInfo(mTaskConfig, mActivityToken, mTaskListener,
false /* isImeShowing */);
- verify(mLayout, never()).updateSurfacePosition();
+ verify(mLayout, never()).updateButtonSurfacePosition();
verify(mLayout, never()).release();
verify(mLayout, never()).createSizeCompatButton(anyBoolean());
@@ -140,7 +169,8 @@
mLayout.updateSizeCompatInfo(newTaskConfiguration, mActivityToken, newTaskListener,
false /* isImeShowing */);
- verify(mLayout).updateSurfacePosition();
+ verify(mLayout).updateButtonSurfacePosition();
+ verify(mLayout).updateHintSurfacePosition();
}
@Test
@@ -152,14 +182,16 @@
mContext.getResources(), false, false);
mLayout.updateDisplayLayout(displayLayout1);
- verify(mLayout).updateSurfacePosition();
+ verify(mLayout).updateButtonSurfacePosition();
+ verify(mLayout).updateHintSurfacePosition();
// No update if the display bounds is the same.
clearInvocations(mLayout);
final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
mContext.getResources(), false, false);
mLayout.updateDisplayLayout(displayLayout2);
- verify(mLayout, never()).updateSurfacePosition();
+ verify(mLayout, never()).updateButtonSurfacePosition();
+ verify(mLayout, never()).updateHintSurfacePosition();
}
@Test
@@ -203,4 +235,29 @@
verify(ActivityClient.getInstance()).restartActivityProcessIfVisible(mActivityToken);
}
+
+ @Test
+ public void testOnRestartButtonLongClicked_showHint() {
+ mLayout.dismissHint();
+
+ assertNull(mLayout.mHint);
+
+ mLayout.onRestartButtonLongClicked();
+
+ assertNotNull(mLayout.mHint);
+ }
+
+ @Test
+ public void testDismissHint() {
+ mLayout.onRestartButtonLongClicked();
+ final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
+ assertNotNull(mLayout.mHint);
+ assertNotNull(hintWindowManager);
+
+ mLayout.dismissHint();
+
+ assertNull(mLayout.mHint);
+ assertNull(mLayout.mHintWindowManager);
+ verify(hintWindowManager).release();
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 5eca3e7..926108c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -41,6 +41,7 @@
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -58,6 +59,7 @@
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
@@ -78,6 +80,8 @@
private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final ShellExecutor mAnimExecutor = new TestShellExecutor();
private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
@@ -90,8 +94,8 @@
@Test
public void testBasicTransitionFlow() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor,
- mAnimExecutor);
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
IBinder transitToken = new Binder();
@@ -109,8 +113,8 @@
@Test
public void testNonDefaultHandler() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor,
- mAnimExecutor);
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
@@ -188,8 +192,8 @@
@Test
public void testRequestRemoteTransition() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor,
- mAnimExecutor);
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final boolean[] remoteCalled = new boolean[]{false};
@@ -255,8 +259,8 @@
@Test
public void testRegisteredRemoteTransition() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor,
- mAnimExecutor);
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final boolean[] remoteCalled = new boolean[]{false};
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index 777aa0b..766714c 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -1,10 +1,6 @@
{
"presubmit": [
{
- "name": "libandroidfw_tests",
- "host": true
- },
- {
"name": "CtsResourcesLoaderTests"
}
]
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
index b36ff09..3035a79 100644
--- a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_libs_androidfw_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_libs_androidfw_license"],
+}
+
cc_fuzz {
name: "cursorwindow_fuzzer",
srcs: [
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index b39f4f2..0bf9480 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -249,5 +249,20 @@
mHead->pendingDirty.setEmpty();
}
+const StretchEffect* DamageAccumulator::findNearestStretchEffect() const {
+ DirtyStack* frame = mHead;
+ while (frame->prev != frame) {
+ frame = frame->prev;
+ if (frame->type == TransformRenderNode) {
+ const auto& effect =
+ frame->renderNode->properties().layerProperties().getStretchEffect();
+ if (!effect.isEmpty()) {
+ return &effect;
+ }
+ }
+ }
+ return nullptr;
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 2faa9d0..89ee0e3 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -35,6 +35,7 @@
struct DirtyStack;
class RenderNode;
class Matrix4;
+class StretchEffect;
class DamageAccumulator {
PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
@@ -62,6 +63,8 @@
void finish(SkRect* totalDirty);
+ const StretchEffect* findNearestStretchEffect() const;
+
private:
void pushCommon();
void applyMatrix4Transform(DirtyStack* frame);
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 5d3f6f2..2448cc9 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -20,13 +20,12 @@
namespace android {
namespace uirenderer {
-const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames = {
+const std::array FrameInfoNames{
"Flags",
"FrameTimelineVsyncId",
"IntendedVsync",
"Vsync",
- "OldestInputEvent",
- "NewestInputEvent",
+ "InputEventId",
"HandleInputStart",
"AnimationStart",
"PerformTraversalsStart",
@@ -40,7 +39,8 @@
"DequeueBufferDuration",
"QueueBufferDuration",
"GpuCompleted",
- "SwapBuffersCompleted"
+ "SwapBuffersCompleted",
+ "DisplayPresentTime",
};
static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 20,
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 45a367f..912d04c5 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -28,15 +28,14 @@
namespace android {
namespace uirenderer {
-#define UI_THREAD_FRAME_INFO_SIZE 11
+static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 10;
enum class FrameInfoIndex {
Flags = 0,
FrameTimelineVsyncId,
IntendedVsync,
Vsync,
- OldestInputEvent,
- NewestInputEvent,
+ InputEventId,
HandleInputStart,
AnimationStart,
PerformTraversalsStart,
@@ -56,13 +55,14 @@
GpuCompleted,
SwapBuffersCompleted,
+ DisplayPresentTime,
// Must be the last value!
// Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT
NumIndexes
};
-extern const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames;
+extern const std::array<const char*, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames;
namespace FrameInfoFlags {
enum {
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 4a2e30d..4eefe92 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -112,7 +112,7 @@
std::lock_guard lock(mDataMutex);
// Fast-path for jank-free frames
- int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
+ int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::SwapBuffersCompleted);
if (mDequeueTimeForgiveness && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) {
nsecs_t expectedDequeueDuration = mDequeueTimeForgiveness + frame[FrameInfoIndex::Vsync] -
frame[FrameInfoIndex::IssueDrawCommandsStart];
@@ -219,7 +219,7 @@
void JankTracker::dumpFrames(int fd) {
dprintf(fd, "\n\n---PROFILEDATA---\n");
for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
- dprintf(fd, "%s", FrameInfoNames[i].c_str());
+ dprintf(fd, "%s", FrameInfoNames[i]);
dprintf(fd, ",");
}
for (size_t i = 0; i < mFrames.size(); i++) {
@@ -236,7 +236,6 @@
}
void JankTracker::reset() {
- std::lock_guard lock(mDataMutex);
mFrames.clear();
mData->reset();
(*mGlobalData)->reset();
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 64b8b71..170f731 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -384,10 +384,11 @@
struct DrawTextBlob final : Op {
static const auto kType = Type::DrawTextBlob;
DrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint)
- : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint) {}
+ : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint), drawTextBlobMode(gDrawTextBlobMode) {}
sk_sp<const SkTextBlob> blob;
SkScalar x, y;
SkPaint paint;
+ DrawTextBlobMode drawTextBlobMode;
void draw(SkCanvas* c, const SkMatrix&) const { c->drawTextBlob(blob.get(), x, y, paint); }
};
@@ -832,6 +833,24 @@
}
}
+template<>
+constexpr color_transform_fn colorTransformForOp<DrawTextBlob>() {
+ return [](const void *opRaw, ColorTransform transform) {
+ const DrawTextBlob *op = reinterpret_cast<const DrawTextBlob*>(opRaw);
+ switch (op->drawTextBlobMode) {
+ case DrawTextBlobMode::HctOutline:
+ const_cast<SkPaint&>(op->paint).setColor(SK_ColorBLACK);
+ break;
+ case DrawTextBlobMode::HctInner:
+ const_cast<SkPaint&>(op->paint).setColor(SK_ColorWHITE);
+ break;
+ default:
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+ break;
+ }
+ };
+}
+
#define X(T) colorTransformForOp<T>(),
static const color_transform_fn color_transform_fns[] = {
#include "DisplayListOps.in"
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 44f54ee..f5b2675 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -226,6 +226,9 @@
if (!mProperties.getAllowForceDark()) {
info.disableForceDark++;
}
+ if (!mProperties.layerProperties().getStretchEffect().isEmpty()) {
+ info.stretchEffectCount++;
+ }
uint32_t animatorDirtyMask = 0;
if (CC_LIKELY(info.runAnimations)) {
@@ -267,6 +270,9 @@
if (!mProperties.getAllowForceDark()) {
info.disableForceDark--;
}
+ if (!mProperties.layerProperties().getStretchEffect().isEmpty()) {
+ info.stretchEffectCount--;
+ }
info.damageAccumulator->popTransform();
}
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 8fba9cf..0589f13 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -70,6 +70,7 @@
setXferMode(other.xferMode());
setColorFilter(other.getColorFilter());
setImageFilter(other.getImageFilter());
+ mStretchEffect = other.mStretchEffect;
return *this;
}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 1fddac4..28d2b4c 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -188,7 +188,7 @@
}
if (mCanvas->getSaveCount() == restoreCount + 1) {
- SkCanvasPriv::DrawBehind(mCanvas, *filterPaint(paint));
+ SkCanvasPriv::DrawBehind(mCanvas, filterPaint(paint));
this->restore();
}
}
@@ -431,15 +431,14 @@
mCanvas->drawColor(color, mode);
}
-SkiaCanvas::PaintCoW&& SkiaCanvas::filterPaint(PaintCoW&& paint) const {
+void SkiaCanvas::onFilterPaint(SkPaint& paint) {
if (mPaintFilter) {
- mPaintFilter->filter(&paint.writeable());
+ mPaintFilter->filter(&paint);
}
- return std::move(paint);
}
void SkiaCanvas::drawPaint(const SkPaint& paint) {
- mCanvas->drawPaint(*filterPaint(paint));
+ mCanvas->drawPaint(filterPaint(paint));
}
// ----------------------------------------------------------------------------
@@ -457,13 +456,11 @@
points += 2;
}
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawPoints(mode, count, pts.get(), p);
- });
+ applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPoints(mode, count, pts.get(), p); });
}
void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) {
- apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); });
+ applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); });
}
void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) {
@@ -472,9 +469,8 @@
void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
const Paint& paint) {
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawLine(startX, startY, stopX, stopY, p);
- });
+ applyLooper(&paint,
+ [&](const SkPaint& p) { mCanvas->drawLine(startX, startY, stopX, stopY, p); });
}
void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) {
@@ -484,46 +480,44 @@
void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const Paint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
- apply_looper(&paint, [&](const SkPaint& p) {
+ applyLooper(&paint, [&](const SkPaint& p) {
mCanvas->drawRect({left, top, right, bottom}, p);
});
}
void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
- apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); });
+ applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); });
}
void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const Paint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawRoundRect(rect, rx, ry, p);
- });
+ applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRoundRect(rect, rx, ry, p); });
}
void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
const Paint& paint) {
- apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); });
+ applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); });
}
void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) {
if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
- apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); });
+ applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); });
}
void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
- apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); });
+ applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); });
}
void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, bool useCenter, const Paint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
- apply_looper(&paint, [&](const SkPaint& p) {
+ applyLooper(&paint, [&](const SkPaint& p) {
if (fabs(sweepAngle) >= 360.0f) {
mCanvas->drawOval(arc, p);
} else {
@@ -537,13 +531,11 @@
if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
return;
}
- apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); });
+ applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); });
}
void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) {
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawVertices(vertices, mode, p);
- });
+ applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawVertices(vertices, mode, p); });
}
// ----------------------------------------------------------------------------
@@ -552,7 +544,7 @@
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
auto image = bitmap.makeImage();
- apply_looper(paint, [&](const SkPaint& p) {
+ applyLooper(paint, [&](const SkPaint& p) {
auto sampling = SkSamplingOptions(p.getFilterQuality());
mCanvas->drawImage(image, left, top, sampling, &p);
});
@@ -562,7 +554,7 @@
auto image = bitmap.makeImage();
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
- apply_looper(paint, [&](const SkPaint& p) {
+ applyLooper(paint, [&](const SkPaint& p) {
auto sampling = SkSamplingOptions(p.getFilterQuality());
mCanvas->drawImage(image, 0, 0, sampling, &p);
});
@@ -575,7 +567,7 @@
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- apply_looper(paint, [&](const SkPaint& p) {
+ applyLooper(paint, [&](const SkPaint& p) {
auto sampling = SkSamplingOptions(p.getFilterQuality());
mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p,
SkCanvas::kFast_SrcRectConstraint);
@@ -672,11 +664,11 @@
pnt.setShader(image->makeShader(sampling));
auto v = builder.detach();
- apply_looper(&pnt, [&](const SkPaint& p) {
+ applyLooper(&pnt, [&](const SkPaint& p) {
SkPaint copy(p);
auto s = SkSamplingOptions(p.getFilterQuality());
if (s != sampling) {
- // apply_looper changed the quality?
+ // applyLooper changed the quality?
copy.setShader(image->makeShader(s));
}
mCanvas->drawVertices(v, SkBlendMode::kModulate, copy);
@@ -707,7 +699,7 @@
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
auto image = bitmap.makeImage();
- apply_looper(paint, [&](const SkPaint& p) {
+ applyLooper(paint, [&](const SkPaint& p) {
auto filter = SkSamplingOptions(p.getFilterQuality()).filter;
mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p);
});
@@ -746,9 +738,7 @@
sk_sp<SkTextBlob> textBlob(builder.make());
- apply_looper(&paintCopy, [&](const SkPaint& p) {
- mCanvas->drawTextBlob(textBlob, 0, 0, p);
- });
+ applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); });
drawTextDecorations(x, y, totalAdvance, paintCopy);
}
@@ -788,9 +778,7 @@
sk_sp<SkTextBlob> textBlob(builder.make());
- apply_looper(&paintCopy, [&](const SkPaint& p) {
- mCanvas->drawTextBlob(textBlob, 0, 0, p);
- });
+ applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); });
}
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index eac3f22..9ab2b10 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -169,53 +169,24 @@
const Paint& paint, const SkPath& path, size_t start,
size_t end) override;
- /** This class acts as a copy on write SkPaint.
- *
- * Initially this will be the SkPaint passed to the contructor.
- * The first time writable() is called this will become a copy of the
- * initial SkPaint (or a default SkPaint if nullptr).
- */
- struct PaintCoW {
- PaintCoW(const SkPaint& that) : mPtr(&that) {}
- PaintCoW(const SkPaint* ptr) : mPtr(ptr) {}
- PaintCoW(const PaintCoW&) = delete;
- PaintCoW(PaintCoW&&) = delete;
- PaintCoW& operator=(const PaintCoW&) = delete;
- PaintCoW& operator=(PaintCoW&&) = delete;
- SkPaint& writeable() {
- if (!mStorage) {
- if (!mPtr) {
- mStorage.emplace();
- } else {
- mStorage.emplace(*mPtr);
- }
- mPtr = &*mStorage;
- }
- return *mStorage;
- }
- operator const SkPaint*() const { return mPtr; }
- const SkPaint* operator->() const { assert(mPtr); return mPtr; }
- explicit operator bool() { return mPtr != nullptr; }
- private:
- const SkPaint* mPtr;
- std::optional<SkPaint> mStorage;
- };
+ void onFilterPaint(SkPaint& paint);
- /** Filters the paint using the current paint filter.
- *
- * @param paint the paint to filter. Will be initialized with the default
- * SkPaint before filtering if filtering is required.
- */
- PaintCoW&& filterPaint(PaintCoW&& paint) const;
+ SkPaint filterPaint(const SkPaint& src) {
+ SkPaint dst(src);
+ this->onFilterPaint(dst);
+ return dst;
+ }
// proc(const SkPaint& modifiedPaint)
- template <typename Proc> void apply_looper(const Paint* paint, Proc proc) {
- SkPaint skp;
- BlurDrawLooper* looper = nullptr;
- if (paint) {
- skp = *filterPaint(paint);
- looper = paint->getLooper();
+ template <typename Proc>
+ void applyLooper(const Paint* paint, Proc proc, void (*preFilter)(SkPaint&) = nullptr) {
+ BlurDrawLooper* looper = paint ? paint->getLooper() : nullptr;
+ const SkPaint* skpPtr = paint;
+ SkPaint skp = skpPtr ? *skpPtr : SkPaint();
+ if (preFilter) {
+ preFilter(skp);
}
+ this->onFilterPaint(skp);
if (looper) {
looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) {
mCanvas->save();
@@ -228,7 +199,6 @@
}
}
-
private:
struct SaveRec {
int saveCount;
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index f2481f8..cc9094c 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -98,6 +98,8 @@
const SkISize screenSize;
+ int stretchEffectCount = 0;
+
struct Out {
bool hasFunctors = false;
// This is only updated if evaluateAnimations is true
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 146bf28..b046f45 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -110,16 +110,19 @@
bool darken = channelSum < (128 * 3);
// outline
+ gDrawTextBlobMode = DrawTextBlobMode::HctOutline;
Paint outlinePaint(paint);
simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
// inner
+ gDrawTextBlobMode = DrawTextBlobMode::HctInner;
Paint innerPaint(paint);
simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
innerPaint.setStyle(SkPaint::kFill_Style);
canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance);
+ gDrawTextBlobMode = DrawTextBlobMode::Normal;
} else {
// standard draw path
canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance);
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index fdfa288..d1bdb71 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -61,6 +61,14 @@
class Paint;
struct Typeface;
+enum class DrawTextBlobMode {
+ Normal,
+ HctOutline,
+ HctInner,
+};
+
+inline DrawTextBlobMode gDrawTextBlobMode = DrawTextBlobMode::Normal;
+
class ANDROID_API Canvas {
public:
virtual ~Canvas(){};
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index df66981..f24ba5c 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -240,8 +240,8 @@
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
- "Mismatched size expectations, given %d expected %d",
- frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
+ "Mismatched size expectations, given %d expected %zu", frameInfoSize,
+ UI_THREAD_FRAME_INFO_SIZE);
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
return proxy->syncAndDrawFrame();
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 8023968..5f60437 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -25,6 +25,7 @@
#include <renderthread/CanvasContext.h>
#endif
#include <TreeInfo.h>
+#include <effects/StretchEffect.h>
#include <hwui/Paint.h>
#include <utils/TraceUtils.h>
@@ -549,6 +550,7 @@
// ----------------------------------------------------------------------------
jmethodID gPositionListener_PositionChangedMethod;
+jmethodID gPositionListener_ApplyStretchMethod;
jmethodID gPositionListener_PositionLostMethod;
static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
@@ -571,6 +573,11 @@
Matrix4 transform;
info.damageAccumulator->computeCurrentTransform(&transform);
const RenderProperties& props = node.properties();
+
+ if (info.stretchEffectCount) {
+ handleStretchEffect(info, transform);
+ }
+
uirenderer::Rect bounds(props.getWidth(), props.getHeight());
transform.mapRect(bounds);
@@ -613,7 +620,7 @@
JNIEnv* env = jnienv();
jobject localref = env->NewLocalRef(mWeakRef);
if (CC_UNLIKELY(!localref)) {
- jnienv()->DeleteWeakGlobalRef(mWeakRef);
+ env->DeleteWeakGlobalRef(mWeakRef);
mWeakRef = nullptr;
return;
}
@@ -634,6 +641,32 @@
return env;
}
+ void handleStretchEffect(const TreeInfo& info, const Matrix4& transform) {
+ // Search up to find the nearest stretcheffect parent
+ const StretchEffect* effect = info.damageAccumulator->findNearestStretchEffect();
+ if (!effect) {
+ return;
+ }
+
+ uirenderer::Rect area = effect->stretchArea;
+ transform.mapRect(area);
+ JNIEnv* env = jnienv();
+
+ jobject localref = env->NewLocalRef(mWeakRef);
+ if (CC_UNLIKELY(!localref)) {
+ env->DeleteWeakGlobalRef(mWeakRef);
+ mWeakRef = nullptr;
+ return;
+ }
+#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
+ env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
+ info.canvasContext.getFrameNumber(), area.left, area.top,
+ area.right, area.bottom, effect->stretchDirection.fX,
+ effect->stretchDirection.fY, effect->maxStretchAmount);
+#endif
+ env->DeleteLocalRef(localref);
+ }
+
void doUpdatePositionAsync(jlong frameNumber, jint left, jint top,
jint right, jint bottom) {
ATRACE_NAME("Update SurfaceView position");
@@ -775,6 +808,8 @@
jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener");
gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz,
"positionChanged", "(JIIII)V");
+ gPositionListener_ApplyStretchMethod =
+ GetMethodIDOrDie(env, clazz, "applyStretch", "(JFFFFFFF)V");
gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz,
"positionLost", "(J)V");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index b288402..af7271e 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -170,55 +170,23 @@
// Recording Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
-SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) {
- bool fixBlending = false;
- bool fixAA = false;
- if (paint) {
- // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
- // older.
- fixBlending = sApiLevel <= 27 && paint->getBlendMode() == SkBlendMode::kClear;
- fixAA = paint->isAntiAlias();
+void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) {
+ // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
+ // older.
+ if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) {
+ paint.setBlendMode(SkBlendMode::kDstOut);
}
- if (fixBlending || fixAA) {
- SkPaint& tmpPaint = paint.writeable();
-
- if (fixBlending) {
- tmpPaint.setBlendMode(SkBlendMode::kDstOut);
- }
-
- // disabling AA on bitmap draws matches legacy HWUI behavior
- tmpPaint.setAntiAlias(false);
- }
-
- return filterPaint(std::move(paint));
+ // disabling AA on bitmap draws matches legacy HWUI behavior
+ paint.setAntiAlias(false);
}
-static BlurDrawLooper* get_looper(const Paint* paint) {
- return paint ? paint->getLooper() : nullptr;
+static SkFilterMode Paint_to_filter(const SkPaint& paint) {
+ return paint.getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
+ : SkFilterMode::kNearest;
}
-template <typename Proc>
-void applyLooper(BlurDrawLooper* looper, const SkPaint* paint, Proc proc) {
- if (looper) {
- SkPaint p;
- if (paint) {
- p = *paint;
- }
- looper->apply(p, [&](SkPoint offset, const SkPaint& modifiedPaint) {
- proc(offset.fX, offset.fY, &modifiedPaint);
- });
- } else {
- proc(0, 0, paint);
- }
-}
-
-static SkFilterMode Paint_to_filter(const SkPaint* paint) {
- return paint && paint->getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
- : SkFilterMode::kNearest;
-}
-
-static SkSamplingOptions Paint_to_sampling(const SkPaint* paint) {
+static SkSamplingOptions Paint_to_sampling(const SkPaint& paint) {
// Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics
return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone);
}
@@ -226,10 +194,12 @@
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
sk_sp<SkImage> image = bitmap.makeImage();
- applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y,
- const SkPaint* p) {
- mRecorder.drawImage(image, left + x, top + y, Paint_to_sampling(p), p, bitmap.palette());
- });
+ applyLooper(
+ paint,
+ [&](const SkPaint& p) {
+ mRecorder.drawImage(image, left, top, Paint_to_sampling(p), &p, bitmap.palette());
+ },
+ FilterForImage);
// if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
// it is not safe to store a raw SkImage pointer, because the image object will be destroyed
@@ -245,10 +215,12 @@
sk_sp<SkImage> image = bitmap.makeImage();
- applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y,
- const SkPaint* p) {
- mRecorder.drawImage(image, x, y, Paint_to_sampling(p), p, bitmap.palette());
- });
+ applyLooper(
+ paint,
+ [&](const SkPaint& p) {
+ mRecorder.drawImage(image, 0, 0, Paint_to_sampling(p), &p, bitmap.palette());
+ },
+ FilterForImage);
if (!bitmap.isImmutable() && image.get() && !image->unique()) {
mDisplayList->mMutableImages.push_back(image.get());
@@ -263,11 +235,13 @@
sk_sp<SkImage> image = bitmap.makeImage();
- applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y,
- const SkPaint* p) {
- mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), Paint_to_sampling(p),
- p, SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
- });
+ applyLooper(
+ paint,
+ [&](const SkPaint& p) {
+ mRecorder.drawImageRect(image, srcRect, dstRect, Paint_to_sampling(p), &p,
+ SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
+ },
+ FilterForImage);
if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
!dstRect.isEmpty()) {
@@ -303,11 +277,12 @@
// HWUI always draws 9-patches with linear filtering, regardless of the Paint.
const SkFilterMode filter = SkFilterMode::kLinear;
- applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y,
- const SkPaint* p) {
- mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), filter, p,
- bitmap.palette());
- });
+ applyLooper(
+ paint,
+ [&](const SkPaint& p) {
+ mRecorder.drawImageLattice(image, lattice, dst, filter, &p, bitmap.palette());
+ },
+ FilterForImage);
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 8d7a21a..ff03e0c 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -87,6 +87,8 @@
std::unique_ptr<SkiaDisplayList> mDisplayList;
StartReorderBarrierDrawable* mCurrentBarrier;
+ static void FilterForImage(SkPaint&);
+
/**
* A new SkiaDisplayList is created or recycled if available.
*
@@ -96,7 +98,7 @@
*/
void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
- PaintCoW&& filterBitmap(PaintCoW&& paint);
+ using INHERITED = SkiaCanvas;
};
} // namespace skiapipeline
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b760db2..f69ddac 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -484,7 +484,8 @@
// TODO(b/165985262): measure performance impact
const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
- const auto inputEventId = mCurrentFrameInfo->get(FrameInfoIndex::NewestInputEvent);
+ const auto inputEventId =
+ static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), vsyncId,
inputEventId);
}
@@ -591,7 +592,6 @@
}
void CanvasContext::finishFrame(FrameInfo* frameInfo) {
-
// TODO (b/169858044): Consolidate this into a single call.
mJankTracker.finishFrame(*frameInfo);
mJankTracker.finishGpuDraw(*frameInfo);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b9568fc..423cc08 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -230,7 +230,10 @@
}
void RenderProxy::resetProfileInfo() {
- mRenderThread.queue().runSync([=]() { mContext->resetFrameStats(); });
+ mRenderThread.queue().runSync([=]() {
+ std::lock_guard lock(mRenderThread.getJankDataMutex());
+ mContext->resetFrameStats();
+ });
}
uint32_t RenderProxy::frameTimePercentile(int percentile) {
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 6a9a98d..898c64b 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -22,16 +22,16 @@
namespace uirenderer {
namespace test {
-const DisplayInfo& getDisplayInfo() {
- static DisplayInfo info = [] {
- DisplayInfo info;
+const ui::StaticDisplayInfo& getDisplayInfo() {
+ static ui::StaticDisplayInfo info = [] {
+ ui::StaticDisplayInfo info;
#if HWUI_NULL_GPU
info.density = 2.f;
#else
const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__);
- const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info);
+ const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &info);
LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get display info", __FUNCTION__);
#endif
return info;
diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h
index 7d2f6d8..9d00366d 100644
--- a/libs/hwui/tests/common/TestContext.h
+++ b/libs/hwui/tests/common/TestContext.h
@@ -23,8 +23,8 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
-#include <ui/DisplayInfo.h>
#include <ui/DisplayMode.h>
+#include <ui/StaticDisplayInfo.h>
#include <utils/Looper.h>
#include <atomic>
@@ -36,7 +36,7 @@
namespace uirenderer {
namespace test {
-const DisplayInfo& getDisplayInfo();
+const ui::StaticDisplayInfo& getDisplayInfo();
const ui::DisplayMode& getActiveDisplayMode();
inline const ui::Size& getActiveDisplayResolution() {
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index ab23448..1a3dbe7 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -33,7 +33,7 @@
constexpr char kRobotoVariable[] = "/system/fonts/Roboto-Regular.ttf";
-constexpr char kRegularFont[] = "/system/fonts/NotoSerif-Regular.ttf";
+constexpr char kRegularFont[] = "/system/fonts/NotoSerif.ttf";
constexpr char kBoldFont[] = "/system/fonts/NotoSerif-Bold.ttf";
constexpr char kItalicFont[] = "/system/fonts/NotoSerif-Italic.ttf";
constexpr char kBoldItalicFont[] = "/system/fonts/NotoSerif-BoldItalic.ttf";
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index e6dfc4c..c0ab58b 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -20,7 +20,6 @@
#include <gui/DisplayEventReceiver.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
-#include <ui/DisplayInfo.h>
#include <utils/BitSet.h>
#include <utils/Looper.h>
#include <utils/RefBase.h>
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 827fcf1..97567ba 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -21,7 +21,6 @@
#include <gui/DisplayEventReceiver.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
-#include <ui/DisplayInfo.h>
#include <utils/BitSet.h>
#include <utils/Looper.h>
#include <utils/RefBase.h>
diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h
index 98073fe..26a65a4 100644
--- a/libs/input/PointerControllerContext.h
+++ b/libs/input/PointerControllerContext.h
@@ -21,7 +21,6 @@
#include <gui/DisplayEventReceiver.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
-#include <ui/DisplayInfo.h>
#include <utils/BitSet.h>
#include <utils/Looper.h>
#include <utils/RefBase.h>
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 3dcaffb..242d9a3 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -1096,13 +1096,7 @@
* Gets the carrier frequency of the tracked signal.
*
* <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
- * L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
- * common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
- *
- * <p> For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
- * measurement objects will be reported for this same satellite, in one of the measurement
- * objects, all the values related to L1 will be filled, and in the other all of the values
- * related to L5 will be filled.
+ * L5 = 1176.45 MHz, varying GLO channels, etc.
*
* <p>The value is only available if {@link #hasCarrierFrequencyHz()} is {@code true}.
*
@@ -1382,7 +1376,8 @@
* <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal. The AGC
* level may be used to indicate potential interference. Higher gain (and/or lower input power)
* shall be output as a positive number. Hence in cases of strong jamming, in the band of this
- * signal, this value will go more negative.
+ * signal, this value will go more negative. This value must be consistent given the same level
+ * of the incoming signal power.
*
* <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
* components) may also affect the typical output of of this value on any given hardware design
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index b46e8ce..23390fc 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -284,12 +284,7 @@
* Gets the carrier frequency of the signal tracked.
*
* <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60
- * MHz, L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
- * common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
- *
- * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two measurements
- * will be reported for this same satellite, in one all the values related to L1 will be
- * filled, and in the other all of the values related to L5 will be filled.
+ * MHz, L5 = 1176.45 MHz, varying GLO channels, etc.
*
* <p>The value is only available if {@link #hasCarrierFrequencyHz(int satelliteIndex)} is
* {@code true}.
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index adf58da..5e39660 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -48,13 +48,13 @@
*/
interface ILocationManager
{
- @nullable Location getLastLocation(String provider, in LastLocationRequest request, String packageName, String attributionTag);
- @nullable ICancellationSignal getCurrentLocation(String provider, in LocationRequest request, in ILocationCallback callback, String packageName, String attributionTag, String listenerId);
+ @nullable Location getLastLocation(String provider, in LastLocationRequest request, String packageName, @nullable String attributionTag);
+ @nullable ICancellationSignal getCurrentLocation(String provider, in LocationRequest request, in ILocationCallback callback, String packageName, @nullable String attributionTag, String listenerId);
- void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId);
+ void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, @nullable String attributionTag, String listenerId);
void unregisterLocationListener(in ILocationListener listener);
- void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent pendingIntent, String packageName, String attributionTag);
+ void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent pendingIntent, String packageName, @nullable String attributionTag);
void unregisterLocationPendingIntent(in PendingIntent pendingIntent);
void injectLocation(in Location location);
@@ -79,24 +79,24 @@
@nullable List<GnssAntennaInfo> getGnssAntennaInfos();
- void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, @nullable String attributionTag);
+ void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, @nullable String attributionTag, String listenerId);
void unregisterGnssStatusCallback(in IGnssStatusListener callback);
- void registerGnssNmeaCallback(in IGnssNmeaListener callback, String packageName, @nullable String attributionTag);
+ void registerGnssNmeaCallback(in IGnssNmeaListener callback, String packageName, @nullable String attributionTag, String listenerId);
void unregisterGnssNmeaCallback(in IGnssNmeaListener callback);
- void addGnssMeasurementsListener(in GnssMeasurementRequest request, in IGnssMeasurementsListener listener, String packageName, @nullable String attributionTag);
+ void addGnssMeasurementsListener(in GnssMeasurementRequest request, in IGnssMeasurementsListener listener, String packageName, @nullable String attributionTag, String listenerId);
void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections);
- void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag);
+ void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag, String listenerId);
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
void addProviderRequestListener(in IProviderRequestListener listener);
void removeProviderRequestListener(in IProviderRequestListener listener);
int getGnssBatchSize();
- void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, @nullable String listenerId);
+ void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, String listenerId);
void flushGnssBatch();
void stopGnssBatch();
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 088b789..dd5b6e6 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -20,7 +20,6 @@
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.LOCATION_HARDWARE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.location.GpsStatus.GPS_EVENT_STARTED;
import static android.location.LocationRequest.createFromDeprecatedCriteria;
import static android.location.LocationRequest.createFromDeprecatedProvider;
@@ -52,7 +51,7 @@
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
-import android.location.provider.ProviderRequest.Listener;
+import android.location.provider.ProviderRequest.ChangedListener;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -412,7 +411,7 @@
private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
"cache_key.location_enabled";
- private static ILocationManager getService() throws RemoteException {
+ static ILocationManager getService() throws RemoteException {
try {
return ILocationManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE));
@@ -439,11 +438,13 @@
new GnssNavigationTransportManager();
}
- private static final ProviderRequestTransportManager sProviderRequestListeners =
- new ProviderRequestTransportManager();
+ private static class ProviderRequestLazyLoader {
+ static final ProviderRequestTransportManager sProviderRequestListeners =
+ new ProviderRequestTransportManager();
+ }
- private final Context mContext;
- private final ILocationManager mService;
+ final Context mContext;
+ final ILocationManager mService;
private volatile PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache =
new PropertyInvalidatedCache<Integer, Boolean>(
@@ -2778,34 +2779,33 @@
}
/**
- * Registers a {@link ProviderRequest.Listener} to all providers.
+ * Adds a {@link ProviderRequest.ChangedListener} for listening to all providers'
+ * {@link ProviderRequest} changed events.
*
* @param executor the executor that the callback runs on
* @param listener the listener to register
- * @return {@code true} always
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
- public boolean registerProviderRequestListener(
+ public void addProviderRequestChangedListener(
@NonNull @CallbackExecutor Executor executor,
- @NonNull Listener listener) {
- sProviderRequestListeners.addListener(listener,
+ @NonNull ChangedListener listener) {
+ ProviderRequestLazyLoader.sProviderRequestListeners.addListener(listener,
new ProviderRequestTransport(executor, listener));
- return true;
}
/**
- * Unregisters a {@link ProviderRequest.Listener}.
+ * Removes a {@link ProviderRequest.ChangedListener} that has been added.
*
* @param listener the listener to remove.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
- public void unregisterProviderRequestListener(
- @NonNull Listener listener) {
- sProviderRequestListeners.removeListener(listener);
+ public void removeProviderRequestChangedListener(
+ @NonNull ProviderRequest.ChangedListener listener) {
+ ProviderRequestLazyLoader.sProviderRequestListeners.removeListener(listener);
}
/**
@@ -2917,11 +2917,16 @@
private static class GnssStatusTransportManager extends
ListenerTransportManager<GnssStatusTransport> {
+ GnssStatusTransportManager() {
+ super(false);
+ }
+
@Override
protected void registerTransport(GnssStatusTransport transport)
throws RemoteException {
getService().registerGnssStatusCallback(transport, transport.getPackage(),
- transport.getAttributionTag());
+ transport.getAttributionTag(),
+ AppOpsManager.toReceiverId(transport.getListener()));
}
@Override
@@ -2934,11 +2939,16 @@
private static class GnssNmeaTransportManager extends
ListenerTransportManager<GnssNmeaTransport> {
+ GnssNmeaTransportManager() {
+ super(false);
+ }
+
@Override
protected void registerTransport(GnssNmeaTransport transport)
throws RemoteException {
getService().registerGnssNmeaCallback(transport, transport.getPackage(),
- transport.getAttributionTag());
+ transport.getAttributionTag(),
+ AppOpsManager.toReceiverId(transport.getListener()));
}
@Override
@@ -2951,11 +2961,16 @@
private static class GnssMeasurementsTransportManager extends
ListenerTransportManager<GnssMeasurementsTransport> {
+ GnssMeasurementsTransportManager() {
+ super(false);
+ }
+
@Override
protected void registerTransport(GnssMeasurementsTransport transport)
throws RemoteException {
getService().addGnssMeasurementsListener(transport.getRequest(), transport,
- transport.getPackage(), transport.getAttributionTag());
+ transport.getPackage(), transport.getAttributionTag(),
+ AppOpsManager.toReceiverId(transport.getListener()));
}
@Override
@@ -2968,6 +2983,10 @@
private static class GnssAntennaTransportManager extends
ListenerTransportManager<GnssAntennaInfoTransport> {
+ GnssAntennaTransportManager() {
+ super(false);
+ }
+
@Override
protected void registerTransport(GnssAntennaInfoTransport transport) {
transport.getContext().registerReceiver(transport,
@@ -2983,11 +3002,16 @@
private static class GnssNavigationTransportManager extends
ListenerTransportManager<GnssNavigationTransport> {
+ GnssNavigationTransportManager() {
+ super(false);
+ }
+
@Override
protected void registerTransport(GnssNavigationTransport transport)
throws RemoteException {
getService().addGnssNavigationMessageListener(transport,
- transport.getPackage(), transport.getAttributionTag());
+ transport.getPackage(), transport.getAttributionTag(),
+ AppOpsManager.toReceiverId(transport.getListener()));
}
@Override
@@ -3000,6 +3024,10 @@
private static class ProviderRequestTransportManager extends
ListenerTransportManager<ProviderRequestTransport> {
+ ProviderRequestTransportManager() {
+ super(false);
+ }
+
@Override
protected void registerTransport(ProviderRequestTransport transport)
throws RemoteException {
@@ -3117,6 +3145,8 @@
}
}
+ /** @deprecated */
+ @Deprecated
private static class GpsAdapter extends GnssStatus.Callback {
private final GpsStatus.Listener mGpsListener;
@@ -3127,7 +3157,7 @@
@Override
public void onStarted() {
- mGpsListener.onGpsStatusChanged(GPS_EVENT_STARTED);
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
}
@Override
@@ -3204,6 +3234,8 @@
}
}
+ /** @deprecated */
+ @Deprecated
private static class GpsStatusTransport extends GnssStatusTransport {
static volatile int sTtff;
@@ -3413,13 +3445,13 @@
}
private static class ProviderRequestTransport extends IProviderRequestListener.Stub
- implements ListenerTransport<ProviderRequest.Listener> {
+ implements ListenerTransport<ChangedListener> {
private final Executor mExecutor;
- private volatile @Nullable ProviderRequest.Listener mListener;
+ private volatile @Nullable ProviderRequest.ChangedListener mListener;
- ProviderRequestTransport(Executor executor, ProviderRequest.Listener listener) {
+ ProviderRequestTransport(Executor executor, ChangedListener listener) {
Preconditions.checkArgument(executor != null, "invalid null executor");
Preconditions.checkArgument(listener != null, "invalid null callback");
mExecutor = executor;
@@ -3432,7 +3464,7 @@
}
@Override
- public @Nullable ProviderRequest.Listener getListener() {
+ public @Nullable ProviderRequest.ChangedListener getListener() {
return mListener;
}
@@ -3442,6 +3474,8 @@
}
}
+ /** @deprecated */
+ @Deprecated
private static class BatchedLocationCallbackWrapper implements LocationListener {
private final BatchedLocationCallback mCallback;
@@ -3461,6 +3495,8 @@
}
}
+ /** @deprecated */
+ @Deprecated
private static class BatchedLocationCallbackTransport extends LocationListenerTransport {
BatchedLocationCallbackTransport(BatchedLocationCallback callback, Handler handler) {
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index b6ec323..b72d365 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -56,10 +56,13 @@
/**
* Listener to be invoked when a new request is set to the provider.
*/
- public interface Listener {
+ public interface ChangedListener {
/**
* Invoked when a new request is set.
+ *
+ * @param provider the location provider associated with the request
+ * @param request the new {@link ProviderRequest}
*/
void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request);
}
diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java
index 0bb7dbb..85a083e 100644
--- a/location/java/android/location/util/identity/CallerIdentity.java
+++ b/location/java/android/location/util/identity/CallerIdentity.java
@@ -42,7 +42,16 @@
@VisibleForTesting
public static CallerIdentity forTest(int uid, int pid, String packageName,
@Nullable String attributionTag) {
- return new CallerIdentity(uid, pid, packageName, attributionTag, null);
+ return forTest(uid, pid, packageName, attributionTag, null);
+ }
+
+ /**
+ * Construct a CallerIdentity for test purposes.
+ */
+ @VisibleForTesting
+ public static CallerIdentity forTest(int uid, int pid, String packageName,
+ @Nullable String attributionTag, @Nullable String listenerId) {
+ return new CallerIdentity(uid, pid, packageName, attributionTag, listenerId);
}
/**
@@ -145,7 +154,10 @@
return mAttributionTag;
}
- /** The calling listener id. */
+ /**
+ * The calling listener id. A null listener id will match any other listener id for the purposes
+ * of {@link #equals(Object)}.
+ */
public String getListenerId() {
return mListenerId;
}
@@ -168,6 +180,17 @@
}
}
+ /**
+ * Returns a CallerIdentity corrosponding to this CallerIdentity but with a null listener id.
+ */
+ public CallerIdentity stripListenerId() {
+ if (mListenerId == null) {
+ return this;
+ } else {
+ return new CallerIdentity(mUid, mPid, mPackageName, mAttributionTag, null);
+ }
+ }
+
@Override
public String toString() {
int length = 10 + mPackageName.length();
@@ -201,15 +224,12 @@
return false;
}
CallerIdentity that = (CallerIdentity) o;
- return equalsIgnoringListenerId(that) && Objects.equals(mListenerId, that.mListenerId);
- }
-
- public boolean equalsIgnoringListenerId(CallerIdentity that) {
- return that != null
- && mUid == that.mUid
+ return mUid == that.mUid
&& mPid == that.mPid
&& mPackageName.equals(that.mPackageName)
- && Objects.equals(mAttributionTag, that.mAttributionTag);
+ && Objects.equals(mAttributionTag, that.mAttributionTag)
+ && (mListenerId == null || that.mListenerId == null || mListenerId.equals(
+ that.mListenerId));
}
@Override
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 205c1f4..383c93d 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -16,8 +16,10 @@
package android.media;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.util.SparseIntArray;
import java.lang.annotation.Retention;
@@ -161,6 +163,14 @@
*/
public static final int TYPE_BLE_SPEAKER = 27;
+ /**
+ * A device type describing an Echo Canceller loopback Reference.
+ * This device is only used when capturing with MediaRecorder.AudioSource.ECHO_REFERENCE,
+ * which requires privileged permission
+ * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT}.
+ * @hide */
+ @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT)
+ public static final int TYPE_ECHO_REFERENCE = 28;
/** @hide */
@IntDef(flag = false, prefix = "TYPE", value = {
@@ -188,7 +198,8 @@
TYPE_FM_TUNER,
TYPE_TV_TUNER,
TYPE_BLE_HEADSET,
- TYPE_BLE_SPEAKER}
+ TYPE_BLE_SPEAKER,
+ TYPE_ECHO_REFERENCE}
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioDeviceType {}
@@ -211,7 +222,8 @@
TYPE_LINE_DIGITAL,
TYPE_IP,
TYPE_BUS,
- TYPE_BLE_HEADSET}
+ TYPE_BLE_HEADSET,
+ TYPE_ECHO_REFERENCE}
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioDeviceTypeIn {}
@@ -297,6 +309,7 @@
case TYPE_BUS:
case TYPE_REMOTE_SUBMIX:
case TYPE_BLE_HEADSET:
+ case TYPE_ECHO_REFERENCE:
return true;
default:
return false;
@@ -621,6 +634,8 @@
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUS, TYPE_BUS);
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_REMOTE_SUBMIX, TYPE_REMOTE_SUBMIX);
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLE_HEADSET, TYPE_BLE_HEADSET);
+ INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_ECHO_REFERENCE, TYPE_ECHO_REFERENCE);
+
// privileges mapping to output device
EXT_TO_INT_DEVICE_MAPPING = new SparseIntArray();
@@ -678,6 +693,9 @@
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_IN_REMOTE_SUBMIX);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_IN_BLE_HEADSET);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_ECHO_REFERENCE, AudioSystem.DEVICE_IN_ECHO_REFERENCE);
+
}
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d896c1f..f87f90d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3123,52 +3123,57 @@
/**
* @hide Home sound
- * Played by the framework when the home app becomes active if config_enableHomeSound is set to
- * true. This is currently only used on TV devices.
+ * <p>
+ * To be played by the framework when the home app becomes active if config_enableHomeSound is
+ * set to true. This is currently only used on TV devices.
* Note that this sound is only available if a sound file is specified in audio_assets.xml.
* @see #playSoundEffect(int)
*/
public static final int FX_HOME = 11;
/**
- * @hide Fast scroll sound 1
- * To be by the framework when a fast-scrolling is performed and
- * {@link #areFastScrollSoundEffectsEnabled()} is true.
+ * @hide Navigation repeat sound 1
+ * <p>
+ * To be played by the framework when a focus navigation is repeatedly triggered
+ * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
* This is currently only used on TV devices.
* Note that this sound is only available if a sound file is specified in audio_assets.xml
* @see #playSoundEffect(int)
*/
- public static final int FX_FAST_SCROLL_1 = 12;
+ public static final int FX_FOCUS_NAVIGATION_REPEAT_1 = 12;
/**
- * @hide Fast scroll sound 2
- * To be by the framework when a fast-scrolling is performed and
- * {@link #areFastScrollSoundEffectsEnabled()} is true.
+ * @hide Navigation repeat sound 2
+ * <p>
+ * To be played by the framework when a focus navigation is repeatedly triggered
+ * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
* This is currently only used on TV devices.
* Note that this sound is only available if a sound file is specified in audio_assets.xml
* @see #playSoundEffect(int)
*/
- public static final int FX_FAST_SCROLL_2 = 13;
+ public static final int FX_FOCUS_NAVIGATION_REPEAT_2 = 13;
/**
- * @hide Fast scroll sound 3
- * To be by the framework when a fast-scrolling is performed and
- * {@link #areFastScrollSoundEffectsEnabled()} is true.
+ * @hide Navigation repeat sound 3
+ * <p>
+ * To be played by the framework when a focus navigation is repeatedly triggered
+ * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
* This is currently only used on TV devices.
* Note that this sound is only available if a sound file is specified in audio_assets.xml
* @see #playSoundEffect(int)
*/
- public static final int FX_FAST_SCROLL_3 = 14;
+ public static final int FX_FOCUS_NAVIGATION_REPEAT_3 = 14;
/**
- * @hide Fast scroll sound 4
- * To be by the framework when a fast-scrolling is performed and
- * {@link #areFastScrollSoundEffectsEnabled()} is true.
+ * @hide Navigation repeat sound 4
+ * <p>
+ * To be played by the framework when a focus navigation is repeatedly triggered
+ * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
* This is currently only used on TV devices.
* Note that this sound is only available if a sound file is specified in audio_assets.xml
* @see #playSoundEffect(int)
*/
- public static final int FX_FAST_SCROLL_4 = 15;
+ public static final int FX_FOCUS_NAVIGATION_REPEAT_4 = 15;
/**
* @hide Number of sound effects
@@ -3177,27 +3182,27 @@
public static final int NUM_SOUND_EFFECTS = 16;
/**
- * @hide Number of fast scroll sound effects
+ * @hide Number of FX_FOCUS_NAVIGATION_REPEAT_* sound effects
*/
- public static final int NUM_FAST_SCROLL_SOUND_EFFECTS = 4;
+ public static final int NUM_NAVIGATION_REPEAT_SOUND_EFFECTS = 4;
/**
* @hide
- * @param n a value in [0, {@link #NUM_FAST_SCROLL_SOUND_EFFECTS}[
- * @return The id of a fast scroll sound effect or -1 if out of bounds
+ * @param n a value in [0, {@link #NUM_NAVIGATION_REPEAT_SOUND_EFFECTS}[
+ * @return The id of a navigation repeat sound effect or -1 if out of bounds
*/
- public static int getNthFastScrollSoundEffectId(int n) {
+ public static int getNthNavigationRepeatSoundEffect(int n) {
switch (n) {
case 0:
- return FX_FAST_SCROLL_1;
+ return FX_FOCUS_NAVIGATION_REPEAT_1;
case 1:
- return FX_FAST_SCROLL_2;
+ return FX_FOCUS_NAVIGATION_REPEAT_2;
case 2:
- return FX_FAST_SCROLL_3;
+ return FX_FOCUS_NAVIGATION_REPEAT_3;
case 3:
- return FX_FAST_SCROLL_4;
+ return FX_FOCUS_NAVIGATION_REPEAT_4;
default:
- Log.w(TAG, "Invalid fast-scroll sound effect id: " + n);
+ Log.w(TAG, "Invalid navigation repeat sound effect id: " + n);
return -1;
}
}
@@ -3205,9 +3210,9 @@
/**
* @hide
*/
- public void setFastScrollSoundEffectsEnabled(boolean enabled) {
+ public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) {
try {
- getService().setFastScrollSoundEffectsEnabled(enabled);
+ getService().setNavigationRepeatSoundEffectsEnabled(enabled);
} catch (RemoteException e) {
}
@@ -3215,11 +3220,11 @@
/**
* @hide
- * @return true if the fast scroll sound effects are enabled
+ * @return true if the navigation repeat sound effects are enabled
*/
- public boolean areFastScrollSoundEffectsEnabled() {
+ public boolean areNavigationRepeatSoundEffectsEnabled() {
try {
- return getService().areFastScrollSoundEffectsEnabled();
+ return getService().areNavigationRepeatSoundEffectsEnabled();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 7fb83f1..6fcb756 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -565,6 +565,11 @@
*/
private int mOffloadPaddingFrames = 0;
+ /**
+ * The log session id used for metrics.
+ */
+ private String mLogSessionId;
+
//--------------------------------
// Used exclusively by native code
//--------------------
@@ -837,6 +842,7 @@
}
baseRegisterPlayer(mSessionId);
+ native_setPlayerIId(mPlayerIId); // mPlayerIId now ready to send to native AudioTrack.
}
/**
@@ -3967,6 +3973,23 @@
}
}
+ /**
+ * Sets a string handle to this AudioTrack for metrics collection.
+ *
+ * @param logSessionId a string which is used to identify this object
+ * to the metrics service.
+ * @throws IllegalStateException if AudioTrack not initialized.
+ *
+ * @hide
+ */
+ public void setLogSessionId(@NonNull String logSessionId) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw new IllegalStateException("track not initialized");
+ }
+ native_setLogSessionId(logSessionId);
+ mLogSessionId = logSessionId;
+ }
+
//---------------------------------------------------------
// Inner classes
//--------------------
@@ -4202,6 +4225,21 @@
private native int native_get_audio_description_mix_level_db(float[] level);
private native int native_set_dual_mono_mode(int dualMonoMode);
private native int native_get_dual_mono_mode(int[] dualMonoMode);
+ private native void native_setLogSessionId(@NonNull String logSessionId);
+
+ /**
+ * Sets the audio service Player Interface Id.
+ *
+ * The playerIId does not change over the lifetime of the client
+ * Java AudioTrack and is set automatically on creation.
+ *
+ * This call informs the native AudioTrack for metrics logging purposes.
+ *
+ * @param id the value reported by AudioManager when registering the track.
+ * A value of -1 indicates invalid - the playerIId was never set.
+ * @throws IllegalStateException if AudioTrack not initialized.
+ */
+ private native void native_setPlayerIId(int playerIId);
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 71ee57e..0073b5c 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -351,9 +351,9 @@
oneway void unregisterCommunicationDeviceDispatcher(
ICommunicationDeviceDispatcher dispatcher);
- boolean areFastScrollSoundEffectsEnabled();
+ boolean areNavigationRepeatSoundEffectsEnabled();
- oneway void setFastScrollSoundEffectsEnabled(boolean enabled);
+ oneway void setNavigationRepeatSoundEffectsEnabled(boolean enabled);
boolean isHomeSoundEffectEnabled();
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 44f8385..9ab4aac 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -131,7 +131,59 @@
*/
public static @NonNull ImageWriter newInstance(@NonNull Surface surface,
@IntRange(from = 1) int maxImages) {
- return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN);
+ return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN, -1 /*width*/,
+ -1 /*height*/);
+ }
+
+ /**
+ * <p>
+ * Create a new ImageWriter with given number of max Images, format and producer dimension.
+ * </p>
+ * <p>
+ * The {@code maxImages} parameter determines the maximum number of
+ * {@link Image} objects that can be be dequeued from the
+ * {@code ImageWriter} simultaneously. Requesting more buffers will use up
+ * more memory, so it is important to use only the minimum number necessary.
+ * </p>
+ * <p>
+ * The format specifies the image format of this ImageWriter. The format
+ * from the {@code surface} will be overridden with this format. For example,
+ * if the surface is obtained from a {@link android.graphics.SurfaceTexture}, the default
+ * format may be {@link PixelFormat#RGBA_8888}. If the application creates an ImageWriter
+ * with this surface and {@link ImageFormat#PRIVATE}, this ImageWriter will be able to operate
+ * with {@link ImageFormat#PRIVATE} Images.
+ * </p>
+ * <p>
+ * Note that the consumer end-point may or may not be able to support Images with different
+ * format, for such case, the application should only use this method if the consumer is able
+ * to consume such images.
+ * </p>
+ * <p> The input Image size can also be set by the client. </p>
+ *
+ * @param surface The destination Surface this writer produces Image data
+ * into.
+ * @param maxImages The maximum number of Images the user will want to
+ * access simultaneously for producing Image data. This should be
+ * as small as possible to limit memory use. Once maxImages
+ * Images are dequeued by the user, one of them has to be queued
+ * back before a new Image can be dequeued for access via
+ * {@link #dequeueInputImage()}.
+ * @param format The format of this ImageWriter. It can be any valid format specified by
+ * {@link ImageFormat} or {@link PixelFormat}.
+ *
+ * @param width Input size width.
+ * @param height Input size height.
+ *
+ * @return a new ImageWriter instance.
+ *
+ * @hide
+ */
+ public static @NonNull ImageWriter newInstance(@NonNull Surface surface,
+ @IntRange(from = 1) int maxImages, @Format int format, int width, int height) {
+ if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
+ throw new IllegalArgumentException("Invalid format is specified: " + format);
+ }
+ return new ImageWriter(surface, maxImages, format, width, height);
}
/**
@@ -180,13 +232,13 @@
if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
throw new IllegalArgumentException("Invalid format is specified: " + format);
}
- return new ImageWriter(surface, maxImages, format);
+ return new ImageWriter(surface, maxImages, format, -1 /*width*/, -1 /*height*/);
}
/**
* @hide
*/
- protected ImageWriter(Surface surface, int maxImages, int format) {
+ protected ImageWriter(Surface surface, int maxImages, int format, int width, int height) {
if (surface == null || maxImages < 1) {
throw new IllegalArgumentException("Illegal input argument: surface " + surface
+ ", maxImages: " + maxImages);
@@ -196,7 +248,8 @@
// Note that the underlying BufferQueue is working in synchronous mode
// to avoid dropping any buffers.
- mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format);
+ mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format, width,
+ height);
// nativeInit internally overrides UNKNOWN format. So does surface format query after
// nativeInit and before getEstimatedNativeAllocBytes().
@@ -919,7 +972,7 @@
// Native implemented ImageWriter methods.
private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs,
- int format);
+ int format, int width, int height);
private synchronized native void nativeClose(long nativeCtx);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index c51c9dd..f3cee17 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.BIND_IMS_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -91,6 +92,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.Vector;
+import java.util.concurrent.Executor;
/**
@@ -2172,7 +2174,7 @@
mOnVideoSizeChangedListener = null;
mOnTimedTextListener = null;
mOnRtpRxNoticeListener = null;
- mOnRtpRxNoticeHandler = null;
+ mOnRtpRxNoticeExecutor = null;
synchronized (mTimeProviderLock) {
if (mTimeProvider != null) {
mTimeProvider.close();
@@ -3711,7 +3713,6 @@
case MEDIA_RTP_RX_NOTICE:
final OnRtpRxNoticeListener rtpRxNoticeListener = mOnRtpRxNoticeListener;
- final Handler rtpRxNoticeHandler = mOnRtpRxNoticeHandler;
if (rtpRxNoticeListener == null) {
return;
}
@@ -3730,14 +3731,9 @@
} finally {
parcel.recycle();
}
- if (rtpRxNoticeHandler == null) {
- rtpRxNoticeListener.onRtpRxNotice(mMediaPlayer, noticeType, data);
- } else {
- rtpRxNoticeHandler.post(
- () ->
- rtpRxNoticeListener
- .onRtpRxNotice(mMediaPlayer, noticeType, data));
- }
+ mOnRtpRxNoticeExecutor.execute(() ->
+ rtpRxNoticeListener
+ .onRtpRxNotice(mMediaPlayer, noticeType, data));
}
return;
@@ -4305,28 +4301,26 @@
*
* @see OnRtpRxNoticeListener
*
- * @param listener the listener called after a notice from RTP Rx
- * @param handler the {@link Handler} that receives RTP Tx events. If null is passed,
- * notifications will be posted on the thread that created this MediaPlayer
- * instance. If the creating thread does not have a {@link Looper}, then
- * notifications will be posted on the main thread.
+ * @param listener the listener called after a notice from RTP Rx.
+ * @param executor the {@link Executor} on which to post RTP Tx events.
* @hide
*/
@SystemApi
@RequiresPermission(BIND_IMS_SERVICE)
public void setOnRtpRxNoticeListener(
@NonNull Context context,
- @NonNull OnRtpRxNoticeListener listener, @Nullable Handler handler) {
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnRtpRxNoticeListener listener) {
Objects.requireNonNull(context);
Preconditions.checkArgument(
context.checkSelfPermission(BIND_IMS_SERVICE) == PERMISSION_GRANTED,
BIND_IMS_SERVICE + " permission not granted.");
mOnRtpRxNoticeListener = Objects.requireNonNull(listener);
- mOnRtpRxNoticeHandler = handler;
+ mOnRtpRxNoticeExecutor = Objects.requireNonNull(executor);
}
private OnRtpRxNoticeListener mOnRtpRxNoticeListener;
- private Handler mOnRtpRxNoticeHandler;
+ private Executor mOnRtpRxNoticeExecutor;
/**
* Register a callback to be invoked when a selected track has timed metadata available.
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 4407efa..5d0f0aa 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -78,7 +78,7 @@
private final int mImplType;
// uniquely identifies the Player Interface throughout the system (P I Id)
- private int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
+ protected int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
@GuardedBy("mLock")
private int mState;
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8de5e42e..aa0f7fd 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -34,6 +34,7 @@
import android.media.VolumeProvider;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
@@ -321,7 +322,7 @@
@NonNull OnActiveSessionsChangedListener sessionListener,
@Nullable ComponentName notificationListener, @Nullable Handler handler) {
addOnActiveSessionsChangedListener(sessionListener, notificationListener,
- UserHandle.myUserId(), handler);
+ UserHandle.myUserId(), handler == null ? null : new HandlerExecutor(handler));
}
/**
@@ -337,38 +338,40 @@
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission in order to
* add listeners for user ids that do not belong to current process.
*
- * @param sessionListener The listener to add.
* @param notificationListener The enabled notification listener component. May be null.
* @param userHandle The user handle to listen for changes on.
- * @param handler The handler to post updates on.
+ * @param executor The executor on which the listener should be invoked
+ * @param sessionListener The listener to add.
* @hide
*/
- @SuppressLint({"ExecutorRegistration", "SamShouldBeLast", "UserHandle"})
+ @SuppressLint("UserHandle")
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void addOnActiveSessionsChangedListener(
- @NonNull OnActiveSessionsChangedListener sessionListener,
- @Nullable ComponentName notificationListener, @NonNull UserHandle userHandle,
- @Nullable Handler handler) {
+ @Nullable ComponentName notificationListener,
+ @NonNull UserHandle userHandle, @NonNull Executor executor,
+ @NonNull OnActiveSessionsChangedListener sessionListener) {
Objects.requireNonNull(userHandle, "userHandle shouldn't be null");
+ Objects.requireNonNull(executor, "executor shouldn't be null");
addOnActiveSessionsChangedListener(sessionListener, notificationListener,
- userHandle.getIdentifier(), handler);
+ userHandle.getIdentifier(), executor);
}
private void addOnActiveSessionsChangedListener(
@NonNull OnActiveSessionsChangedListener sessionListener,
@Nullable ComponentName notificationListener, int userId,
- @Nullable Handler handler) {
+ @Nullable Executor executor) {
Objects.requireNonNull(sessionListener, "sessionListener shouldn't be null");
- if (handler == null) {
- handler = new Handler();
+ if (executor == null) {
+ executor = new HandlerExecutor(new Handler());
}
+
synchronized (mLock) {
if (mListeners.get(sessionListener) != null) {
Log.w(TAG, "Attempted to add session listener twice, ignoring.");
return;
}
SessionsChangedWrapper wrapper = new SessionsChangedWrapper(mContext, sessionListener,
- handler);
+ executor);
try {
mService.addSessionsListener(wrapper.mStub, notificationListener, userId);
mListeners.put(sessionListener, wrapper);
@@ -412,7 +415,8 @@
*/
public void addOnSession2TokensChangedListener(
@NonNull OnSession2TokensChangedListener listener) {
- addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, new Handler());
+ addOnSession2TokensChangedListener(UserHandle.myUserId(), listener,
+ new HandlerExecutor(new Handler()));
}
/**
@@ -428,7 +432,9 @@
*/
public void addOnSession2TokensChangedListener(
@NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) {
- addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler);
+ Objects.requireNonNull(handler, "handler shouldn't be null");
+ addOnSession2TokensChangedListener(UserHandle.myUserId(), listener,
+ new HandlerExecutor(handler));
}
/**
@@ -445,20 +451,19 @@
*
* @param userHandle The userHandle to listen for changes on
* @param listener The listener to add
- * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
- * be used.
+ * @param executor The executor on which the listener should be invoked
* @hide
*/
@SuppressLint("UserHandle")
public void addOnSession2TokensChangedListener(@NonNull UserHandle userHandle,
- @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) {
+ @NonNull OnSession2TokensChangedListener listener, @NonNull Executor executor) {
Objects.requireNonNull(userHandle, "userHandle shouldn't be null");
- addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, handler);
+ Objects.requireNonNull(executor, "executor shouldn't be null");
+ addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, executor);
}
private void addOnSession2TokensChangedListener(int userId,
- OnSession2TokensChangedListener listener, Handler handler) {
- Objects.requireNonNull(handler, "handler shouldn't be null");
+ OnSession2TokensChangedListener listener, Executor executor) {
Objects.requireNonNull(listener, "listener shouldn't be null");
synchronized (mLock) {
if (mSession2TokensListeners.get(listener) != null) {
@@ -466,7 +471,7 @@
return;
}
Session2TokensChangedWrapper wrapper =
- new Session2TokensChangedWrapper(listener, handler);
+ new Session2TokensChangedWrapper(listener, executor);
try {
mService.addSession2TokensListener(wrapper.getStub(), userId);
mSession2TokensListeners.put(listener, wrapper);
@@ -847,7 +852,7 @@
/**
* Add a {@link OnMediaKeyEventDispatchedListener}.
*
- * @param executor The executor on which the callback should be invoked
+ * @param executor The executor on which the listener should be invoked
* @param listener A {@link OnMediaKeyEventDispatchedListener}.
* @hide
*/
@@ -898,7 +903,7 @@
/**
* Add a {@link OnMediaKeyEventDispatchedListener}.
*
- * @param executor The executor on which the callback should be invoked
+ * @param executor The executor on which the listener should be invoked
* @param listener A {@link OnMediaKeyEventSessionChangedListener}.
* @hide
*/
@@ -1257,62 +1262,61 @@
private static final class SessionsChangedWrapper {
private Context mContext;
private OnActiveSessionsChangedListener mListener;
- private Handler mHandler;
+ private Executor mExecutor;
public SessionsChangedWrapper(Context context, OnActiveSessionsChangedListener listener,
- Handler handler) {
+ Executor executor) {
mContext = context;
mListener = listener;
- mHandler = handler;
+ mExecutor = executor;
}
private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() {
@Override
public void onActiveSessionsChanged(final List<MediaSession.Token> tokens) {
- final Handler handler = mHandler;
- if (handler != null) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- final Context context = mContext;
- if (context != null) {
- ArrayList<MediaController> controllers = new ArrayList<>();
- int size = tokens.size();
- for (int i = 0; i < size; i++) {
- controllers.add(new MediaController(context, tokens.get(i)));
- }
- final OnActiveSessionsChangedListener listener = mListener;
- if (listener != null) {
- listener.onActiveSessionsChanged(controllers);
- }
- }
- }
- });
+ if (mExecutor != null) {
+ final Executor executor = mExecutor;
+ executor.execute(() -> callOnActiveSessionsChangedListener(tokens));
}
}
};
+ private void callOnActiveSessionsChangedListener(final List<MediaSession.Token> tokens) {
+ final Context context = mContext;
+ if (context != null) {
+ ArrayList<MediaController> controllers = new ArrayList<>();
+ int size = tokens.size();
+ for (int i = 0; i < size; i++) {
+ controllers.add(new MediaController(context, tokens.get(i)));
+ }
+ final OnActiveSessionsChangedListener listener = mListener;
+ if (listener != null) {
+ listener.onActiveSessionsChanged(controllers);
+ }
+ }
+ }
+
private void release() {
mListener = null;
mContext = null;
- mHandler = null;
+ mExecutor = null;
}
}
private static final class Session2TokensChangedWrapper {
private final OnSession2TokensChangedListener mListener;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final ISession2TokensListener.Stub mStub =
new ISession2TokensListener.Stub() {
@Override
public void onSession2TokensChanged(final List<Session2Token> tokens) {
- mHandler.post(() -> mListener.onSession2TokensChanged(tokens));
+ mExecutor.execute(() -> mListener.onSession2TokensChanged(tokens));
}
};
- Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Handler handler) {
+ Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Executor executor) {
mListener = listener;
- mHandler = (handler == null) ? new Handler() : new Handler(handler.getLooper());
+ mExecutor = executor;
}
public ISession2TokensListener.Stub getStub() {
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index ee0be01..952bbf5 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -149,7 +149,7 @@
/**
* Invalid 64-bit filter ID.
*/
- public static final long INVALID_FILTER_ID_64BIT =
+ public static final long INVALID_FILTER_ID_LONG =
android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_FILTER_ID_64BIT;
/**
* Invalid frequency that is used as the default frontend frequency setting.
@@ -932,8 +932,8 @@
public int connectFrontendToCiCam(int ciCamId) {
if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
"linkFrontendToCiCam")) {
- if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)
- && checkCiCamResource(ciCamId)) {
+ if (checkCiCamResource(ciCamId)
+ && checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
return nativeLinkCiCam(ciCamId);
}
}
@@ -978,7 +978,8 @@
public int disconnectFrontendToCiCam(int ciCamId) {
if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
"unlinkFrontendToCiCam")) {
- if (mFrontendCiCamHandle != null && mFrontendCiCamId == ciCamId) {
+ if (mFrontendCiCamHandle != null && mFrontendCiCamId != null
+ && mFrontendCiCamId == ciCamId) {
int result = nativeUnlinkCiCam(ciCamId);
if (result == RESULT_SUCCESS) {
mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
@@ -1409,6 +1410,7 @@
boolean granted = mTunerResourceManager.requestCiCam(request, ciCamHandle);
if (granted) {
mFrontendCiCamHandle = ciCamHandle[0];
+ mFrontendCiCamId = ciCamId;
}
return granted;
}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadEvent.java b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
index 9f97b61..394211be 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadEvent.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner.filter;
+import android.annotation.IntRange;
import android.annotation.SystemApi;
/**
@@ -51,6 +52,7 @@
/**
* Gets MPU sequence number of filtered data.
*/
+ @IntRange(from = 0)
public int getMpuSequenceNumber() {
return mMpuSequenceNumber;
}
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 51b685a..2f3e2d8 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -309,7 +309,8 @@
}
/**
- * Gets the filter Id.
+ * Gets the filter Id in 32-bit. For any Tuner SoC that supports 64-bit filter architecture,
+ * use {@link #getIdLong()}.
*/
public int getId() {
synchronized (mLock) {
@@ -319,9 +320,10 @@
}
/**
- * Gets the 64-bit filter Id.
+ * Gets the 64-bit filter Id. For any Tuner SoC that supports 32-bit filter architecture,
+ * use {@link #getId()}.
*/
- public long getId64Bit() {
+ public long getIdLong() {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
return nativeGetId64Bit();
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 91be5c3..dbd85e9 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.filter;
import android.annotation.BytesLong;
+import android.annotation.IntRange;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.media.MediaCodec.LinearBlock;
@@ -154,6 +155,7 @@
/**
* Gets MPU sequence number of filtered data.
*/
+ @IntRange(from = 0)
public int getMpuSequenceNumber() {
return mMpuSequenceNumber;
}
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 6a41c74..58a81d9 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.filter;
import android.annotation.BytesLong;
+import android.annotation.IntRange;
import android.annotation.SystemApi;
import android.media.tv.tuner.filter.RecordSettings.ScHevcIndex;
@@ -69,6 +70,7 @@
* {@link android.media.tv.tuner.TunerVersionChecker#getTunerVersion()} to get the version
* information.
*/
+ @IntRange(from = 0)
public int getMpuSequenceNumber() {
return mMpuSequenceNumber;
}
diff --git a/media/java/android/media/tv/tuner/filter/PesEvent.java b/media/java/android/media/tv/tuner/filter/PesEvent.java
index 695e596..bfb7460 100644
--- a/media/java/android/media/tv/tuner/filter/PesEvent.java
+++ b/media/java/android/media/tv/tuner/filter/PesEvent.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner.filter;
+import android.annotation.IntRange;
import android.annotation.SystemApi;
/**
@@ -53,6 +54,7 @@
/**
* Gets MPU sequence number of filtered data.
*/
+ @IntRange(from = 0)
public int getMpuSequenceNumber() {
return mMpuSequenceNumber;
}
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 5d959a3..b291ac95b 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -364,7 +364,7 @@
}
static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface,
- jint maxImages, jint userFormat) {
+ jint maxImages, jint userFormat, jint userWidth, jint userHeight) {
status_t res;
ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages);
@@ -405,20 +405,38 @@
// Get the dimension and format of the producer.
sp<ANativeWindow> anw = producer;
int32_t width, height, surfaceFormat;
- if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) {
- ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res);
- jniThrowRuntimeException(env, "Failed to query Surface width");
- return 0;
+ if (userWidth < 0) {
+ if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) {
+ ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+ jniThrowRuntimeException(env, "Failed to query Surface width");
+ return 0;
+ }
+ } else {
+ width = userWidth;
}
+
ctx->setBufferWidth(width);
- if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) {
- ALOGE("%s: Query Surface height failed: %s (%d)", __FUNCTION__, strerror(-res), res);
- jniThrowRuntimeException(env, "Failed to query Surface height");
- return 0;
+ if (userHeight < 0) {
+ if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) {
+ ALOGE("%s: Query Surface height failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+ jniThrowRuntimeException(env, "Failed to query Surface height");
+ return 0;
+ }
+ } else {
+ height = userHeight;
}
ctx->setBufferHeight(height);
+ if ((userWidth > 0) && (userHeight > 0)) {
+ res = native_window_set_buffers_user_dimensions(anw.get(), userWidth, userHeight);
+ if (res != OK) {
+ ALOGE("%s: Set buffer dimensions failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+ jniThrowRuntimeException(env, "Set buffer dimensions failed");
+ return 0;
+ }
+ }
+
// Query surface format if no valid user format is specified, otherwise, override surface format
// with user format.
if (userFormat == IMAGE_FORMAT_UNKNOWN) {
@@ -1045,7 +1063,7 @@
static JNINativeMethod gImageWriterMethods[] = {
{"nativeClassInit", "()V", (void*)ImageWriter_classInit },
- {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;II)J",
+ {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIII)J",
(void*)ImageWriter_init },
{"nativeClose", "(J)V", (void*)ImageWriter_close },
{"nativeAttachAndQueueImage", "(JJIJIIIIII)I", (void*)ImageWriter_attachAndQueueImage },
diff --git a/media/jni/android_media_JetPlayer.cpp b/media/jni/android_media_JetPlayer.cpp
index 481f80b..10a5b58 100644
--- a/media/jni/android_media_JetPlayer.cpp
+++ b/media/jni/android_media_JetPlayer.cpp
@@ -43,12 +43,34 @@
jfieldID nativePlayerInJavaObj; // stores in Java the native JetPlayer object
};
-static fields_t javaJetPlayerFields;
+static fields_t javaJetPlayerFields {};
+#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
+#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
+
+static void initializeJavaIDs(JNIEnv* env) {
+ static std::once_flag sJniInitialized;
+
+ std::call_once(sJniInitialized, [](JNIEnv* env) {
+ // Get the JetPlayer java class
+ jclass jetPlayerClass = FindClassOrDie(env, kClassPathName);
+ javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass);
+
+ // Get the mNativePlayerInJavaObj variable field
+ javaJetPlayerFields.nativePlayerInJavaObj =
+ GetFieldIDOrDie(env, jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J");
+
+ // Get the callback to post events from this native code to Java
+ javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
+ javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME,
+ "(Ljava/lang/Object;III)V");
+ }, env);
+}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
+
/*
* This function is called from JetPlayer instance's render thread
*/
@@ -79,6 +101,8 @@
android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jint maxTracks, jint trackBufferSize)
{
+ initializeJavaIDs(env);
+
//ALOGV("android_media_JetPlayer_setup(): entering.");
JetPlayer* lpJet = new JetPlayer(env->NewGlobalRef(weak_this), maxTracks, trackBufferSize);
@@ -511,28 +535,9 @@
{"native_clearQueue", "()Z", (void *)android_media_JetPlayer_clearQueue},
};
-#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
-#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
int register_android_media_JetPlayer(JNIEnv *env)
{
- javaJetPlayerFields.jetClass = NULL;
- javaJetPlayerFields.postNativeEventInJava = NULL;
- javaJetPlayerFields.nativePlayerInJavaObj = NULL;
-
- // Get the JetPlayer java class
- jclass jetPlayerClass = FindClassOrDie(env, kClassPathName);
- javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass);
-
- // Get the mNativePlayerInJavaObj variable field
- javaJetPlayerFields.nativePlayerInJavaObj = GetFieldIDOrDie(env,
- jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J");
-
- // Get the callback to post events from this native code to Java
- javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
- javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME,
- "(Ljava/lang/Object;III)V");
-
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 71c86cc..1870a93 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -61,6 +61,7 @@
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/PersistentSurface.h>
+#include <mediadrm/DrmUtils.h>
#include <mediadrm/ICrypto.h>
#include <private/android/AHardwareBufferHelpers.h>
@@ -312,6 +313,7 @@
mGraphicOutput = (mime.startsWithIgnoreCase("video/") || mime.startsWithIgnoreCase("image/"))
&& !(flags & CONFIGURE_FLAG_ENCODE);
mHasCryptoOrDescrambler = (crypto != nullptr) || (descrambler != nullptr);
+ mCrypto = crypto;
return mCodec->configure(
format, mSurfaceTextureClient, crypto, descrambler, flags);
@@ -1103,6 +1105,8 @@
}
}
+jint MediaErrorToJavaError(status_t err);
+
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -1150,7 +1154,8 @@
env->Throw(exception);
}
-static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
+static void throwCryptoException(JNIEnv *env, status_t err, const char *msg,
+ const sp<ICrypto> &crypto) {
ScopedLocalRef<jclass> clazz(
env, env->FindClass("android/media/MediaCodec$CryptoException"));
CHECK(clazz.get() != NULL);
@@ -1159,7 +1164,7 @@
env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
CHECK(constructID != NULL);
- const char *defaultMsg = "Unknown Error";
+ std::string defaultMsg = "Unknown Error";
/* translate OS errors to Java API CryptoException errorCodes (which are positive) */
switch (err) {
@@ -1199,11 +1204,17 @@
err = gCryptoErrorCodes.cryptoErrorLostState;
defaultMsg = "Session state was lost, open a new session and retry";
break;
- default: /* Other negative DRM error codes go out as is. */
+ default: /* Other negative DRM error codes go out best-effort. */
+ err = MediaErrorToJavaError(err);
+ defaultMsg = StrCryptoError(err);
break;
}
- jstring msgObj = env->NewStringUTF(msg != NULL ? msg : defaultMsg);
+ std::string msgStr(msg != NULL ? msg : defaultMsg.c_str());
+ if (crypto != NULL) {
+ msgStr = DrmUtils::GetExceptionMessage(err, msgStr.c_str(), crypto);
+ }
+ jstring msgObj = env->NewStringUTF(msgStr.c_str());
jthrowable exception =
(jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
@@ -1213,7 +1224,7 @@
static jint throwExceptionAsNecessary(
JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL,
- const char *msg = NULL) {
+ const char *msg = NULL, const sp<ICrypto>& crypto = NULL) {
switch (err) {
case OK:
return 0;
@@ -1237,7 +1248,7 @@
default:
if (isCryptoError(err)) {
- throwCryptoException(env, err, msg);
+ throwCryptoException(env, err, msg, crypto);
return 0;
}
throwCodecException(env, err, actionCode, msg);
@@ -1899,7 +1910,8 @@
subSamples = NULL;
throwExceptionAsNecessary(
- env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
+ env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str(),
+ codec->getCrypto());
}
static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jclass, jobject bufferObj) {
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index a58f9a7..f16bcf3 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -164,6 +164,8 @@
bool hasCryptoOrDescrambler() { return mHasCryptoOrDescrambler; }
+ const sp<ICrypto> &getCrypto() { return mCrypto; }
+
protected:
virtual ~JMediaCodec();
@@ -193,6 +195,8 @@
status_t mInitStatus;
+ sp<ICrypto> mCrypto;
+
template <typename T>
status_t createByteBufferFromABuffer(
JNIEnv *env, bool readOnly, bool clearBuffer, const sp<T> &buffer,
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index 517672e..f491be8 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -202,10 +202,11 @@
uuid = NULL;
if (err != OK) {
- jniThrowException(
+ std::string strerr(StrCryptoError(err));
+ jniThrowExceptionFmt(
env,
"android/media/MediaCryptoException",
- "Failed to instantiate crypto object.");
+ "Failed to instantiate crypto object: %s", strerr.c_str());
return;
}
@@ -295,7 +296,8 @@
} else if (err == NO_INIT) {
msg += ": crypto plugin not initialized";
} else {
- msg.appendFormat(": general failure (%d)", err);
+ std::string strerr(StrCryptoError(err));
+ msg.appendFormat(": general failure (%s)", strerr.c_str());
}
jniThrowException(env, "android/media/MediaCryptoException", msg.string());
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 73e1f7d..56f6c45 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -424,7 +424,7 @@
static bool throwExceptionAsNecessary(
JNIEnv *env, const sp<IDrm> &drm, status_t err, const char *msg = NULL) {
std::string msgStr;
- if (drm != NULL) {
+ if (drm != NULL && err != OK) {
msgStr = DrmUtils::GetExceptionMessage(err, msg, drm);
msg = msgStr.c_str();
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index eee9f1e..3669bf4 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -217,23 +217,34 @@
void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) {
ALOGD("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->CallVoidMethod(
- mLnbObj,
- gFields.onLnbEventID,
- (jint)lnbEventType);
+ jobject lnb(env->NewLocalRef(mLnbObj));
+ if (!env->IsSameObject(lnb, nullptr)) {
+ env->CallVoidMethod(
+ lnb,
+ gFields.onLnbEventID,
+ (jint)lnbEventType);
+ } else {
+ ALOGE("LnbClientCallbackImpl::onEvent:"
+ "Lnb object has been freed. Ignoring callback.");
+ }
}
void LnbClientCallbackImpl::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) {
ALOGD("LnbClientCallbackImpl::onDiseqcMessage");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jbyteArray array = env->NewByteArray(diseqcMessage.size());
- env->SetByteArrayRegion(
- array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0]));
-
- env->CallVoidMethod(
- mLnbObj,
- gFields.onLnbDiseqcMessageID,
- array);
+ jobject lnb(env->NewLocalRef(mLnbObj));
+ if (!env->IsSameObject(lnb, nullptr)) {
+ jbyteArray array = env->NewByteArray(diseqcMessage.size());
+ env->SetByteArrayRegion(
+ array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0]));
+ env->CallVoidMethod(
+ lnb,
+ gFields.onLnbDiseqcMessageID,
+ array);
+ } else {
+ ALOGE("LnbClientCallbackImpl::onDiseqcMessage:"
+ "Lnb object has been freed. Ignoring callback.");
+ }
}
void LnbClientCallbackImpl::setLnb(jweak lnbObj) {
@@ -254,19 +265,31 @@
void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) {
ALOGD("DvrClientCallbackImpl::onRecordStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->CallVoidMethod(
- mDvrObj,
- gFields.onDvrRecordStatusID,
- (jint) status);
+ jobject dvr(env->NewLocalRef(mDvrObj));
+ if (!env->IsSameObject(dvr, nullptr)) {
+ env->CallVoidMethod(
+ dvr,
+ gFields.onDvrRecordStatusID,
+ (jint) status);
+ } else {
+ ALOGE("DvrClientCallbackImpl::onRecordStatus:"
+ "Dvr object has been freed. Ignoring callback.");
+ }
}
void DvrClientCallbackImpl::onPlaybackStatus(PlaybackStatus status) {
ALOGD("DvrClientCallbackImpl::onPlaybackStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->CallVoidMethod(
- mDvrObj,
- gFields.onDvrPlaybackStatusID,
- (jint) status);
+ jobject dvr(env->NewLocalRef(mDvrObj));
+ if (!env->IsSameObject(dvr, nullptr)) {
+ env->CallVoidMethod(
+ dvr,
+ gFields.onDvrPlaybackStatusID,
+ (jint) status);
+ } else {
+ ALOGE("DvrClientCallbackImpl::onPlaybackStatus:"
+ "Dvr object has been freed. Ignoring callback.");
+ }
}
void DvrClientCallbackImpl::setDvr(jweak dvrObj) {
@@ -810,10 +833,16 @@
}
}
}
- env->CallVoidMethod(
- mFilterObj,
- gFields.onFilterEventID,
- array);
+ jobject filter(env->NewLocalRef(mFilterObj));
+ if (!env->IsSameObject(filter, nullptr)) {
+ env->CallVoidMethod(
+ filter,
+ gFields.onFilterEventID,
+ array);
+ } else {
+ ALOGE("FilterClientCallbackImpl::onFilterEvent_1_1:"
+ "Filter object has been freed. Ignoring callback.");
+ }
}
void FilterClientCallbackImpl::onFilterEvent(const DemuxFilterEvent& filterEvent) {
@@ -828,10 +857,16 @@
void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
ALOGD("FilterClientCallbackImpl::onFilterStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->CallVoidMethod(
- mFilterObj,
- gFields.onFilterStatusID,
- (jint)status);
+ jobject filter(env->NewLocalRef(mFilterObj));
+ if (!env->IsSameObject(filter, nullptr)) {
+ env->CallVoidMethod(
+ filter,
+ gFields.onFilterStatusID,
+ (jint)status);
+ } else {
+ ALOGE("FilterClientCallbackImpl::onFilterStatus:"
+ "Filter object has been freed. Ignoring callback.");
+ }
}
void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filterClient) {
@@ -841,6 +876,15 @@
mFilterClient = filterClient;
}
+FilterClientCallbackImpl::~FilterClientCallbackImpl() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (mFilterObj != NULL) {
+ env->DeleteWeakGlobalRef(mFilterObj);
+ mFilterObj = NULL;
+ }
+ mFilterClient = NULL;
+}
+
/////////////// FrontendClientCallbackImpl ///////////////////////
FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject(tunerObj) {}
@@ -848,10 +892,16 @@
void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) {
ALOGD("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->CallVoidMethod(
- mObject,
- gFields.onFrontendEventID,
- (jint)frontendEventType);
+ jobject frontend(env->NewLocalRef(mObject));
+ if (!env->IsSameObject(frontend, nullptr)) {
+ env->CallVoidMethod(
+ frontend,
+ gFields.onFrontendEventID,
+ (jint)frontendEventType);
+ } else {
+ ALOGE("FrontendClientCallbackImpl::onEvent:"
+ "Frontend object has been freed. Ignoring callback.");
+ }
}
void FrontendClientCallbackImpl::onScanMessage(
@@ -859,11 +909,17 @@
ALOGD("FrontendClientCallbackImpl::onScanMessage, type=%d", type);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
+ jobject frontend(env->NewLocalRef(mObject));
+ if (env->IsSameObject(frontend, nullptr)) {
+ ALOGE("FrontendClientCallbackImpl::onScanMessage:"
+ "Frontend object has been freed. Ignoring callback.");
+ return;
+ }
switch(type) {
case FrontendScanMessageType::LOCKED: {
if (message.isLocked()) {
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onLocked", "()V"));
}
break;
@@ -871,14 +927,14 @@
case FrontendScanMessageType::END: {
if (message.isEnd()) {
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onScanStopped", "()V"));
}
break;
}
case FrontendScanMessageType::PROGRESS_PERCENT: {
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onProgress", "(I)V"),
(jint) message.progressPercent());
break;
@@ -889,7 +945,7 @@
env->SetIntArrayRegion(freqs, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onFrequenciesReport", "([I)V"),
freqs);
break;
@@ -900,21 +956,21 @@
env->SetIntArrayRegion(symbolRates, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onSymbolRates", "([I)V"),
symbolRates);
break;
}
case FrontendScanMessageType::HIERARCHY: {
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onHierarchy", "(I)V"),
(jint) message.hierarchy());
break;
}
case FrontendScanMessageType::ANALOG_TYPE: {
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onSignalType", "(I)V"),
(jint) message.analogType());
break;
@@ -926,7 +982,7 @@
env->SetIntArrayRegion(plpIds, 0, jintV.size(), &jintV[0]);
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onPlpIds", "([I)V"),
plpIds);
break;
@@ -938,7 +994,7 @@
env->SetIntArrayRegion(groupIds, 0, jintV.size(), &jintV[0]);
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onGroupIds", "([I)V"),
groupIds);
break;
@@ -950,7 +1006,7 @@
env->SetIntArrayRegion(streamIds, 0, jintV.size(), &jintV[0]);
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onInputStreamIds", "([I)V"),
streamIds);
break;
@@ -961,21 +1017,21 @@
if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sStd) {
standard = (jint) std.sStd();
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onDvbsStandard", "(I)V"),
standard);
} else if (std.getDiscriminator() ==
FrontendScanMessage::Standard::hidl_discriminator::tStd) {
standard = (jint) std.tStd();
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onDvbtStandard", "(I)V"),
standard);
} else if (std.getDiscriminator() ==
FrontendScanMessage::Standard::hidl_discriminator::sifStd) {
standard = (jint) std.sifStd();
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onAnalogSifStandard", "(I)V"),
standard);
}
@@ -996,7 +1052,7 @@
env->SetObjectArrayElement(array, i, obj);
}
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onAtsc3PlpInfos",
"([Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;)V"),
array);
@@ -1010,6 +1066,12 @@
ALOGD("FrontendClientCallbackImpl::onScanMessageExt1_1, type=%d", type);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
+ jobject frontend(env->NewLocalRef(mObject));
+ if (env->IsSameObject(frontend, nullptr)) {
+ ALOGE("FrontendClientCallbackImpl::onScanMessageExt1_1:"
+ "Frontend object has been freed. Ignoring callback.");
+ return;
+ }
switch(type) {
case FrontendScanMessageTypeExt1_1::MODULATION: {
jint modulation = -1;
@@ -1056,7 +1118,7 @@
}
if (modulation > 0) {
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onModulationReported", "(I)V"),
modulation);
}
@@ -1065,7 +1127,7 @@
case FrontendScanMessageTypeExt1_1::HIGH_PRIORITY: {
bool isHighPriority = message.isHighPriority();
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onPriorityReported", "(B)V"),
isHighPriority);
break;
@@ -1073,7 +1135,7 @@
case FrontendScanMessageTypeExt1_1::DVBC_ANNEX: {
jint dvbcAnnex = (jint) message.annex();
env->CallVoidMethod(
- mObject,
+ frontend,
env->GetMethodID(clazz, "onDvbcAnnexReported", "(I)V"),
dvbcAnnex);
break;
@@ -1083,6 +1145,14 @@
}
}
+FrontendClientCallbackImpl::~FrontendClientCallbackImpl() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (mObject != NULL) {
+ env->DeleteWeakGlobalRef(mObject);
+ mObject = NULL;
+ }
+}
+
/////////////// Tuner ///////////////////////
sp<TunerClient> JTuner::mTunerClient;
@@ -1158,15 +1228,22 @@
if (mDemuxClient != NULL) {
mDemuxClient->setFrontendDataSource(mFeClient);
}
- sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject);
- mFeClient->setCallback(feClientCb);
JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jobject tuner(env->NewLocalRef(mObject));
+ if (env->IsSameObject(tuner, nullptr)) {
+ ALOGE("openFrontendByHandle"
+ "Tuner object has been freed. Failed to open frontend.");
+ return NULL;
+ }
+
+ sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject);
+ mFeClient->setCallback(feClientCb);
// TODO: add more fields to frontend
return env->NewObject(
env->FindClass("android/media/tv/tuner/Tuner$Frontend"),
gFields.frontendInitID,
- mObject,
+ tuner,
(jint) mFeId);
}
@@ -1714,16 +1791,14 @@
dvrObj =
env->NewObject(
env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"),
- gFields.dvrRecorderInitID,
- mObject);
+ gFields.dvrRecorderInitID);
dvrClient->incStrong(dvrObj);
env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrClient.get());
} else {
dvrObj =
env->NewObject(
env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"),
- gFields.dvrPlaybackInitID,
- mObject);
+ gFields.dvrPlaybackInitID);
dvrClient->incStrong(dvrObj);
env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrClient.get());
}
@@ -2126,6 +2201,10 @@
intBandwidth = static_cast<jint>(bandwidth.dvbt());
break;
}
+ case FrontendBandwidth::hidl_discriminator::dvbc: {
+ intBandwidth = static_cast<jint>(bandwidth.dvbc());
+ break;
+ }
case FrontendBandwidth::hidl_discriminator::isdbt: {
intBandwidth = static_cast<jint>(bandwidth.isdbt());
break;
@@ -2646,12 +2725,10 @@
FrontendDvbsVcmMode vcmMode =
static_cast<FrontendDvbsVcmMode>(
env->GetIntField(settings, env->GetFieldID(clazz, "mVcmMode", "I")));
- FrontendDvbsCodeRate coderate = getDvbsCodeRate(env, settings);
FrontendDvbsSettings frontendDvbsSettings {
.frequency = freq,
.modulation = modulation,
- .coderate = coderate,
.symbolRate = symbolRate,
.rolloff = rolloff,
.pilot = pilot,
@@ -2659,6 +2736,13 @@
.standard = standard,
.vcmMode = vcmMode,
};
+
+ jobject jcodeRate = env->GetObjectField(settings, env->GetFieldID(clazz, "mCodeRate",
+ "Landroid/media/tv/tuner/frontend/DvbsCodeRate;"));
+ if (jcodeRate != NULL) {
+ frontendDvbsSettings.coderate = getDvbsCodeRate(env, settings);
+ }
+
frontendSettings.dvbs(frontendDvbsSettings);
return frontendSettings;
}
@@ -2670,7 +2754,7 @@
static_cast<FrontendDvbsScanType>(
env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I")));
bool isDiseqcRxMessage = static_cast<bool>(env->GetBooleanField(
- settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "B")));
+ settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "Z")));
FrontendDvbsSettingsExt1_1 dvbsExt1_1 {
.scanType = scanType,
@@ -2910,6 +2994,10 @@
static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
ALOGD("getFrontendSettings %d", type);
+ if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
+ return FrontendSettings();
+ }
+
FrontendType feType = static_cast<FrontendType>(type);
switch(feType) {
case FrontendType::ANALOG:
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 0e30b18e..fafef42 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -118,6 +118,7 @@
};
struct FilterClientCallbackImpl : public FilterClientCallback {
+ ~FilterClientCallbackImpl();
virtual void onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
const DemuxFilterEventExt& filterEventExt);
virtual void onFilterEvent(const DemuxFilterEvent& filterEvent);
@@ -155,7 +156,7 @@
struct FrontendClientCallbackImpl : public FrontendClientCallback {
FrontendClientCallbackImpl(jweak tunerObj);
-
+ ~FrontendClientCallbackImpl();
virtual void onEvent(FrontendEventType frontendEventType);
virtual void onScanMessage(
FrontendScanMessageType type, const FrontendScanMessage& message);
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 4efdcac..ffed474 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -32,6 +32,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
+#include "core_jni_helpers.h"
#include <jni.h>
#include <media/stagefright/NuMediaExtractor.h>
#include <nativehelper/JNIHelp.h>
@@ -48,6 +49,7 @@
// ----------------------------------------------------------------------------
+// MtpDatabase methods
static jmethodID method_beginSendObject;
static jmethodID method_endSendObject;
static jmethodID method_rescanFile;
@@ -75,6 +77,7 @@
static jmethodID method_getObjectReferences;
static jmethodID method_setObjectReferences;
+// MtpDatabase fields.
static jfieldID field_context;
// MtpPropertyList methods
@@ -86,6 +89,59 @@
static jmethodID method_getLongValues;
static jmethodID method_getStringValues;
+// Initializer for the jfieldIDs and jmethodIDs above. This method must be invoked
+// before using these static fields and methods for JNI accesses.
+static void initializeJavaIDs(JNIEnv* env) {
+ static std::once_flag sJniInitialized;
+
+#define GET_METHOD_ID(name, jclass, signature) \
+ method_##name = GetMethodIDOrDie(env, jclass, #name, signature);
+
+ std::call_once(sJniInitialized, [](JNIEnv* env) {
+ const jclass mdb_class = FindClassOrDie(env, "android/mtp/MtpDatabase");
+ GET_METHOD_ID(beginSendObject, mdb_class, "(Ljava/lang/String;III)I");
+ GET_METHOD_ID(endSendObject, mdb_class, "(IZ)V");
+ GET_METHOD_ID(rescanFile, mdb_class, "(Ljava/lang/String;II)V");
+ GET_METHOD_ID(getObjectList, mdb_class, "(III)[I");
+ GET_METHOD_ID(getNumObjects, mdb_class, "(III)I");
+ GET_METHOD_ID(getSupportedPlaybackFormats, mdb_class, "()[I");
+ GET_METHOD_ID(getSupportedCaptureFormats, mdb_class, "()[I");
+ GET_METHOD_ID(getSupportedObjectProperties, mdb_class, "(I)[I");
+ GET_METHOD_ID(getSupportedDeviceProperties, mdb_class, "()[I");
+ GET_METHOD_ID(setObjectProperty, mdb_class, "(IIJLjava/lang/String;)I");
+ GET_METHOD_ID(getDeviceProperty, mdb_class, "(I[J[C)I");
+ GET_METHOD_ID(setDeviceProperty, mdb_class, "(IJLjava/lang/String;)I");
+ GET_METHOD_ID(getObjectPropertyList, mdb_class, "(IIIII)Landroid/mtp/MtpPropertyList;");
+ GET_METHOD_ID(getObjectInfo, mdb_class, "(I[I[C[J)Z");
+ GET_METHOD_ID(getObjectFilePath, mdb_class, "(I[C[J)I");
+ GET_METHOD_ID(openFilePath, mdb_class, "(Ljava/lang/String;Z)I");
+ GET_METHOD_ID(getThumbnailInfo, mdb_class, "(I[J)Z");
+ GET_METHOD_ID(getThumbnailData, mdb_class, "(I)[B");
+ GET_METHOD_ID(beginDeleteObject, mdb_class, "(I)I");
+ GET_METHOD_ID(endDeleteObject, mdb_class, "(IZ)V");
+ GET_METHOD_ID(beginMoveObject, mdb_class, "(III)I");
+ GET_METHOD_ID(endMoveObject, mdb_class, "(IIIIIZ)V");
+ GET_METHOD_ID(beginCopyObject, mdb_class, "(III)I");
+ GET_METHOD_ID(endCopyObject, mdb_class, "(IZ)V");
+ GET_METHOD_ID(getObjectReferences, mdb_class, "(I)[I");
+ GET_METHOD_ID(setObjectReferences, mdb_class, "(I[I)I");
+ field_context = GetFieldIDOrDie(env, mdb_class, "mNativeContext", "J");
+
+ const jclass mpl_class = FindClassOrDie(env, "android/mtp/MtpPropertyList");
+ GET_METHOD_ID(getCode, mpl_class, "()I");
+ GET_METHOD_ID(getCount, mpl_class, "()I");
+ GET_METHOD_ID(getObjectHandles, mpl_class, "()[I");
+ GET_METHOD_ID(getPropertyCodes, mpl_class, "()[I");
+ GET_METHOD_ID(getDataTypes, mpl_class, "()[I");
+ GET_METHOD_ID(getLongValues, mpl_class, "()[J");
+ GET_METHOD_ID(getStringValues, mpl_class, "()[Ljava/lang/String;");
+
+ return 0;
+ }, env);
+
+#undef GET_METHOD_ID
+}
+
IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
return (IMtpDatabase *)env->GetLongField(database, field_context);
@@ -1280,6 +1336,7 @@
static void
android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
{
+ initializeJavaIDs(env);
MtpDatabase* database = new MtpDatabase(env, thiz);
env->SetLongField(thiz, field_context, (jlong)database);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -1314,69 +1371,9 @@
{"format_date_time", "(J)Ljava/lang/String;",
(void *)android_mtp_MtpPropertyGroup_format_date_time},
};
-
-#define GET_METHOD_ID(name, jclass, signature) \
- method_##name = env->GetMethodID(jclass, #name, signature); \
- if (method_##name == NULL) { \
- ALOGE("Can't find " #name); \
- return -1; \
- } \
-
+ \
int register_android_mtp_MtpDatabase(JNIEnv *env)
{
- jclass clazz;
-
- clazz = env->FindClass("android/mtp/MtpDatabase");
- if (clazz == NULL) {
- ALOGE("Can't find android/mtp/MtpDatabase");
- return -1;
- }
- GET_METHOD_ID(beginSendObject, clazz, "(Ljava/lang/String;III)I");
- GET_METHOD_ID(endSendObject, clazz, "(IZ)V");
- GET_METHOD_ID(rescanFile, clazz, "(Ljava/lang/String;II)V");
- GET_METHOD_ID(getObjectList, clazz, "(III)[I");
- GET_METHOD_ID(getNumObjects, clazz, "(III)I");
- GET_METHOD_ID(getSupportedPlaybackFormats, clazz, "()[I");
- GET_METHOD_ID(getSupportedCaptureFormats, clazz, "()[I");
- GET_METHOD_ID(getSupportedObjectProperties, clazz, "(I)[I");
- GET_METHOD_ID(getSupportedDeviceProperties, clazz, "()[I");
- GET_METHOD_ID(setObjectProperty, clazz, "(IIJLjava/lang/String;)I");
- GET_METHOD_ID(getDeviceProperty, clazz, "(I[J[C)I");
- GET_METHOD_ID(setDeviceProperty, clazz, "(IJLjava/lang/String;)I");
- GET_METHOD_ID(getObjectPropertyList, clazz, "(IIIII)Landroid/mtp/MtpPropertyList;");
- GET_METHOD_ID(getObjectInfo, clazz, "(I[I[C[J)Z");
- GET_METHOD_ID(getObjectFilePath, clazz, "(I[C[J)I");
- GET_METHOD_ID(openFilePath, clazz, "(Ljava/lang/String;Z)I");
- GET_METHOD_ID(getThumbnailInfo, clazz, "(I[J)Z");
- GET_METHOD_ID(getThumbnailData, clazz, "(I)[B");
- GET_METHOD_ID(beginDeleteObject, clazz, "(I)I");
- GET_METHOD_ID(endDeleteObject, clazz, "(IZ)V");
- GET_METHOD_ID(beginMoveObject, clazz, "(III)I");
- GET_METHOD_ID(endMoveObject, clazz, "(IIIIIZ)V");
- GET_METHOD_ID(beginCopyObject, clazz, "(III)I");
- GET_METHOD_ID(endCopyObject, clazz, "(IZ)V");
- GET_METHOD_ID(getObjectReferences, clazz, "(I)[I");
- GET_METHOD_ID(setObjectReferences, clazz, "(I[I)I");
-
- field_context = env->GetFieldID(clazz, "mNativeContext", "J");
- if (field_context == NULL) {
- ALOGE("Can't find MtpDatabase.mNativeContext");
- return -1;
- }
-
- clazz = env->FindClass("android/mtp/MtpPropertyList");
- if (clazz == NULL) {
- ALOGE("Can't find android/mtp/MtpPropertyList");
- return -1;
- }
- GET_METHOD_ID(getCode, clazz, "()I");
- GET_METHOD_ID(getCount, clazz, "()I");
- GET_METHOD_ID(getObjectHandles, clazz, "()[I");
- GET_METHOD_ID(getPropertyCodes, clazz, "()[I");
- GET_METHOD_ID(getDataTypes, clazz, "()[I");
- GET_METHOD_ID(getLongValues, clazz, "()[J");
- GET_METHOD_ID(getStringValues, clazz, "()[Ljava/lang/String;");
-
if (AndroidRuntime::registerNativeMethods(env,
"android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
return -1;
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 060eaf9..3d2b00fe 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -34,6 +34,7 @@
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
+#include "core_jni_helpers.h"
#include "nativehelper/ScopedLocalRef.h"
#include "private/android_filesystem_config.h"
@@ -107,6 +108,95 @@
static jfieldID field_event_parameter2;
static jfieldID field_event_parameter3;
+// Initializer for the jclasses, jfieldIDs and jmethodIDs declared above. This method must be
+// invoked before using these static fields for JNI accesses.
+static void initializeJavaIDs(JNIEnv* env) {
+ static std::once_flag sJniInitialized;
+
+ std::call_once(sJniInitialized, [](JNIEnv* env) {
+ clazz_deviceInfo =
+ (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpDeviceInfo"));
+ constructor_deviceInfo = GetMethodIDOrDie(env, clazz_deviceInfo, "<init>", "()V");
+ field_deviceInfo_manufacturer =
+ GetFieldIDOrDie(env, clazz_deviceInfo, "mManufacturer", "Ljava/lang/String;");
+ field_deviceInfo_model =
+ GetFieldIDOrDie(env, clazz_deviceInfo, "mModel", "Ljava/lang/String;");
+ field_deviceInfo_version =
+ GetFieldIDOrDie(env, clazz_deviceInfo, "mVersion", "Ljava/lang/String;");
+ field_deviceInfo_serialNumber =
+ GetFieldIDOrDie(env, clazz_deviceInfo, "mSerialNumber", "Ljava/lang/String;");
+ field_deviceInfo_operationsSupported =
+ GetFieldIDOrDie(env, clazz_deviceInfo, "mOperationsSupported", "[I");
+ field_deviceInfo_eventsSupported =
+ GetFieldIDOrDie(env, clazz_deviceInfo, "mEventsSupported", "[I");
+
+ clazz_storageInfo =
+ (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpStorageInfo"));
+ constructor_storageInfo = GetMethodIDOrDie(env, clazz_storageInfo, "<init>", "()V");
+ field_storageInfo_storageId = GetFieldIDOrDie(env, clazz_storageInfo, "mStorageId", "I");
+ field_storageInfo_maxCapacity =
+ GetFieldIDOrDie(env, clazz_storageInfo, "mMaxCapacity", "J");
+ field_storageInfo_freeSpace =
+ GetFieldIDOrDie(env, clazz_storageInfo, "mFreeSpace", "J");
+ field_storageInfo_description =
+ GetFieldIDOrDie(env, clazz_storageInfo, "mDescription", "Ljava/lang/String;");
+ field_storageInfo_volumeIdentifier =
+ GetFieldIDOrDie(env, clazz_storageInfo, "mVolumeIdentifier", "Ljava/lang/String;");
+
+ clazz_objectInfo =
+ (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpObjectInfo"));
+ constructor_objectInfo = GetMethodIDOrDie(env, clazz_objectInfo, "<init>", "()V");
+ field_objectInfo_handle = GetFieldIDOrDie(env, clazz_objectInfo, "mHandle", "I");
+ field_objectInfo_storageId = GetFieldIDOrDie(env, clazz_objectInfo, "mStorageId", "I");
+ field_objectInfo_format = GetFieldIDOrDie(env, clazz_objectInfo, "mFormat", "I");
+ field_objectInfo_protectionStatus =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mProtectionStatus", "I");
+ field_objectInfo_compressedSize =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mCompressedSize", "I");
+ field_objectInfo_thumbFormat = GetFieldIDOrDie(env, clazz_objectInfo, "mThumbFormat", "I");
+ field_objectInfo_thumbCompressedSize =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mThumbCompressedSize", "I");
+ field_objectInfo_thumbPixWidth =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixWidth", "I");
+ field_objectInfo_thumbPixHeight =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixHeight", "I");
+ field_objectInfo_imagePixWidth =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixWidth", "I");
+ field_objectInfo_imagePixHeight =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixHeight", "I");
+ field_objectInfo_imagePixDepth =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixDepth", "I");
+ field_objectInfo_parent = GetFieldIDOrDie(env, clazz_objectInfo, "mParent", "I");
+ field_objectInfo_associationType =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationType", "I");
+ field_objectInfo_associationDesc =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationDesc", "I");
+ field_objectInfo_sequenceNumber =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mSequenceNumber", "I");
+ field_objectInfo_name =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mName", "Ljava/lang/String;");
+ field_objectInfo_dateCreated = GetFieldIDOrDie(env, clazz_objectInfo, "mDateCreated", "J");
+ field_objectInfo_dateModified =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mDateModified", "J");
+ field_objectInfo_keywords =
+ GetFieldIDOrDie(env, clazz_objectInfo, "mKeywords", "Ljava/lang/String;");
+
+ clazz_event = (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpEvent"));
+ constructor_event = GetMethodIDOrDie(env, clazz_event, "<init>", "()V");
+ field_event_eventCode = GetFieldIDOrDie(env, clazz_event, "mEventCode", "I");
+ field_event_parameter1 = GetFieldIDOrDie(env, clazz_event, "mParameter1", "I");
+ field_event_parameter2 = GetFieldIDOrDie(env, clazz_event, "mParameter2", "I");
+ field_event_parameter3 = GetFieldIDOrDie(env, clazz_event, "mParameter3", "I");
+
+ const jclass clazz = FindClassOrDie(env, "android/mtp/MtpDevice");
+ field_context = GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
+
+ clazz_io_exception = (jclass)env->NewGlobalRef(FindClassOrDie(env, "java/io/IOException"));
+ clazz_operation_canceled_exception =
+ (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/os/OperationCanceledException"));
+ }, env);
+}
+
class JavaArrayWriter {
public:
JavaArrayWriter(JNIEnv* env, jbyteArray array) :
@@ -132,6 +222,11 @@
MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice)
{
+ // get_device_from_object() is called by the majority of JNI methods in this file. Call
+ // `initializeJavaIDs()` here to ensure JNI methodID's, fieldIDs and classes are initialized
+ // before use.
+ initializeJavaIDs(env);
+
return (MtpDevice*)env->GetLongField(javaDevice, field_context);
}
@@ -200,6 +295,8 @@
MtpDevice* device = MtpDevice::open(deviceNameStr, fd);
env->ReleaseStringUTFChars(deviceName, deviceNameStr);
+ // The jfieldID `field_context` needs to be initialized before use below.
+ initializeJavaIDs(env);
if (device)
env->SetLongField(thiz, field_context, (jlong)device);
return (jboolean)(device != NULL);
@@ -781,256 +878,7 @@
int register_android_mtp_MtpDevice(JNIEnv *env)
{
- jclass clazz;
-
ALOGD("register_android_mtp_MtpDevice\n");
-
- clazz = env->FindClass("android/mtp/MtpDeviceInfo");
- if (clazz == NULL) {
- ALOGE("Can't find android/mtp/MtpDeviceInfo");
- return -1;
- }
- constructor_deviceInfo = env->GetMethodID(clazz, "<init>", "()V");
- if (constructor_deviceInfo == NULL) {
- ALOGE("Can't find android/mtp/MtpDeviceInfo constructor");
- return -1;
- }
- field_deviceInfo_manufacturer = env->GetFieldID(clazz, "mManufacturer", "Ljava/lang/String;");
- if (field_deviceInfo_manufacturer == NULL) {
- ALOGE("Can't find MtpDeviceInfo.mManufacturer");
- return -1;
- }
- field_deviceInfo_model = env->GetFieldID(clazz, "mModel", "Ljava/lang/String;");
- if (field_deviceInfo_model == NULL) {
- ALOGE("Can't find MtpDeviceInfo.mModel");
- return -1;
- }
- field_deviceInfo_version = env->GetFieldID(clazz, "mVersion", "Ljava/lang/String;");
- if (field_deviceInfo_version == NULL) {
- ALOGE("Can't find MtpDeviceInfo.mVersion");
- return -1;
- }
- field_deviceInfo_serialNumber = env->GetFieldID(clazz, "mSerialNumber", "Ljava/lang/String;");
- if (field_deviceInfo_serialNumber == NULL) {
- ALOGE("Can't find MtpDeviceInfo.mSerialNumber");
- return -1;
- }
- field_deviceInfo_operationsSupported = env->GetFieldID(clazz, "mOperationsSupported", "[I");
- if (field_deviceInfo_operationsSupported == NULL) {
- ALOGE("Can't find MtpDeviceInfo.mOperationsSupported");
- return -1;
- }
- field_deviceInfo_eventsSupported = env->GetFieldID(clazz, "mEventsSupported", "[I");
- if (field_deviceInfo_eventsSupported == NULL) {
- ALOGE("Can't find MtpDeviceInfo.mEventsSupported");
- return -1;
- }
- clazz_deviceInfo = (jclass)env->NewGlobalRef(clazz);
-
- clazz = env->FindClass("android/mtp/MtpStorageInfo");
- if (clazz == NULL) {
- ALOGE("Can't find android/mtp/MtpStorageInfo");
- return -1;
- }
- constructor_storageInfo = env->GetMethodID(clazz, "<init>", "()V");
- if (constructor_storageInfo == NULL) {
- ALOGE("Can't find android/mtp/MtpStorageInfo constructor");
- return -1;
- }
- field_storageInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I");
- if (field_storageInfo_storageId == NULL) {
- ALOGE("Can't find MtpStorageInfo.mStorageId");
- return -1;
- }
- field_storageInfo_maxCapacity = env->GetFieldID(clazz, "mMaxCapacity", "J");
- if (field_storageInfo_maxCapacity == NULL) {
- ALOGE("Can't find MtpStorageInfo.mMaxCapacity");
- return -1;
- }
- field_storageInfo_freeSpace = env->GetFieldID(clazz, "mFreeSpace", "J");
- if (field_storageInfo_freeSpace == NULL) {
- ALOGE("Can't find MtpStorageInfo.mFreeSpace");
- return -1;
- }
- field_storageInfo_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
- if (field_storageInfo_description == NULL) {
- ALOGE("Can't find MtpStorageInfo.mDescription");
- return -1;
- }
- field_storageInfo_volumeIdentifier = env->GetFieldID(clazz, "mVolumeIdentifier", "Ljava/lang/String;");
- if (field_storageInfo_volumeIdentifier == NULL) {
- ALOGE("Can't find MtpStorageInfo.mVolumeIdentifier");
- return -1;
- }
- clazz_storageInfo = (jclass)env->NewGlobalRef(clazz);
-
- clazz = env->FindClass("android/mtp/MtpObjectInfo");
- if (clazz == NULL) {
- ALOGE("Can't find android/mtp/MtpObjectInfo");
- return -1;
- }
- constructor_objectInfo = env->GetMethodID(clazz, "<init>", "()V");
- if (constructor_objectInfo == NULL) {
- ALOGE("Can't find android/mtp/MtpObjectInfo constructor");
- return -1;
- }
- field_objectInfo_handle = env->GetFieldID(clazz, "mHandle", "I");
- if (field_objectInfo_handle == NULL) {
- ALOGE("Can't find MtpObjectInfo.mHandle");
- return -1;
- }
- field_objectInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I");
- if (field_objectInfo_storageId == NULL) {
- ALOGE("Can't find MtpObjectInfo.mStorageId");
- return -1;
- }
- field_objectInfo_format = env->GetFieldID(clazz, "mFormat", "I");
- if (field_objectInfo_format == NULL) {
- ALOGE("Can't find MtpObjectInfo.mFormat");
- return -1;
- }
- field_objectInfo_protectionStatus = env->GetFieldID(clazz, "mProtectionStatus", "I");
- if (field_objectInfo_protectionStatus == NULL) {
- ALOGE("Can't find MtpObjectInfo.mProtectionStatus");
- return -1;
- }
- field_objectInfo_compressedSize = env->GetFieldID(clazz, "mCompressedSize", "I");
- if (field_objectInfo_compressedSize == NULL) {
- ALOGE("Can't find MtpObjectInfo.mCompressedSize");
- return -1;
- }
- field_objectInfo_thumbFormat = env->GetFieldID(clazz, "mThumbFormat", "I");
- if (field_objectInfo_thumbFormat == NULL) {
- ALOGE("Can't find MtpObjectInfo.mThumbFormat");
- return -1;
- }
- field_objectInfo_thumbCompressedSize = env->GetFieldID(clazz, "mThumbCompressedSize", "I");
- if (field_objectInfo_thumbCompressedSize == NULL) {
- ALOGE("Can't find MtpObjectInfo.mThumbCompressedSize");
- return -1;
- }
- field_objectInfo_thumbPixWidth = env->GetFieldID(clazz, "mThumbPixWidth", "I");
- if (field_objectInfo_thumbPixWidth == NULL) {
- ALOGE("Can't find MtpObjectInfo.mThumbPixWidth");
- return -1;
- }
- field_objectInfo_thumbPixHeight = env->GetFieldID(clazz, "mThumbPixHeight", "I");
- if (field_objectInfo_thumbPixHeight == NULL) {
- ALOGE("Can't find MtpObjectInfo.mThumbPixHeight");
- return -1;
- }
- field_objectInfo_imagePixWidth = env->GetFieldID(clazz, "mImagePixWidth", "I");
- if (field_objectInfo_imagePixWidth == NULL) {
- ALOGE("Can't find MtpObjectInfo.mImagePixWidth");
- return -1;
- }
- field_objectInfo_imagePixHeight = env->GetFieldID(clazz, "mImagePixHeight", "I");
- if (field_objectInfo_imagePixHeight == NULL) {
- ALOGE("Can't find MtpObjectInfo.mImagePixHeight");
- return -1;
- }
- field_objectInfo_imagePixDepth = env->GetFieldID(clazz, "mImagePixDepth", "I");
- if (field_objectInfo_imagePixDepth == NULL) {
- ALOGE("Can't find MtpObjectInfo.mImagePixDepth");
- return -1;
- }
- field_objectInfo_parent = env->GetFieldID(clazz, "mParent", "I");
- if (field_objectInfo_parent == NULL) {
- ALOGE("Can't find MtpObjectInfo.mParent");
- return -1;
- }
- field_objectInfo_associationType = env->GetFieldID(clazz, "mAssociationType", "I");
- if (field_objectInfo_associationType == NULL) {
- ALOGE("Can't find MtpObjectInfo.mAssociationType");
- return -1;
- }
- field_objectInfo_associationDesc = env->GetFieldID(clazz, "mAssociationDesc", "I");
- if (field_objectInfo_associationDesc == NULL) {
- ALOGE("Can't find MtpObjectInfo.mAssociationDesc");
- return -1;
- }
- field_objectInfo_sequenceNumber = env->GetFieldID(clazz, "mSequenceNumber", "I");
- if (field_objectInfo_sequenceNumber == NULL) {
- ALOGE("Can't find MtpObjectInfo.mSequenceNumber");
- return -1;
- }
- field_objectInfo_name = env->GetFieldID(clazz, "mName", "Ljava/lang/String;");
- if (field_objectInfo_name == NULL) {
- ALOGE("Can't find MtpObjectInfo.mName");
- return -1;
- }
- field_objectInfo_dateCreated = env->GetFieldID(clazz, "mDateCreated", "J");
- if (field_objectInfo_dateCreated == NULL) {
- ALOGE("Can't find MtpObjectInfo.mDateCreated");
- return -1;
- }
- field_objectInfo_dateModified = env->GetFieldID(clazz, "mDateModified", "J");
- if (field_objectInfo_dateModified == NULL) {
- ALOGE("Can't find MtpObjectInfo.mDateModified");
- return -1;
- }
- field_objectInfo_keywords = env->GetFieldID(clazz, "mKeywords", "Ljava/lang/String;");
- if (field_objectInfo_keywords == NULL) {
- ALOGE("Can't find MtpObjectInfo.mKeywords");
- return -1;
- }
- clazz_objectInfo = (jclass)env->NewGlobalRef(clazz);
-
- clazz = env->FindClass("android/mtp/MtpEvent");
- if (clazz == NULL) {
- ALOGE("Can't find android/mtp/MtpEvent");
- return -1;
- }
- constructor_event = env->GetMethodID(clazz, "<init>", "()V");
- if (constructor_event == NULL) {
- ALOGE("Can't find android/mtp/MtpEvent constructor");
- return -1;
- }
- field_event_eventCode = env->GetFieldID(clazz, "mEventCode", "I");
- if (field_event_eventCode == NULL) {
- ALOGE("Can't find MtpObjectInfo.mEventCode");
- return -1;
- }
- field_event_parameter1 = env->GetFieldID(clazz, "mParameter1", "I");
- if (field_event_parameter1 == NULL) {
- ALOGE("Can't find MtpObjectInfo.mParameter1");
- return -1;
- }
- field_event_parameter2 = env->GetFieldID(clazz, "mParameter2", "I");
- if (field_event_parameter2 == NULL) {
- ALOGE("Can't find MtpObjectInfo.mParameter2");
- return -1;
- }
- field_event_parameter3 = env->GetFieldID(clazz, "mParameter3", "I");
- if (field_event_parameter3 == NULL) {
- ALOGE("Can't find MtpObjectInfo.mParameter3");
- return -1;
- }
- clazz_event = (jclass)env->NewGlobalRef(clazz);
-
- clazz = env->FindClass("android/mtp/MtpDevice");
- if (clazz == NULL) {
- ALOGE("Can't find android/mtp/MtpDevice");
- return -1;
- }
- field_context = env->GetFieldID(clazz, "mNativeContext", "J");
- if (field_context == NULL) {
- ALOGE("Can't find MtpDevice.mNativeContext");
- return -1;
- }
- clazz = env->FindClass("java/io/IOException");
- if (clazz == NULL) {
- ALOGE("Can't find java.io.IOException");
- return -1;
- }
- clazz_io_exception = (jclass)env->NewGlobalRef(clazz);
- clazz = env->FindClass("android/os/OperationCanceledException");
- if (clazz == NULL) {
- ALOGE("Can't find android.os.OperationCanceledException");
- return -1;
- }
- clazz_operation_canceled_exception = (jclass)env->NewGlobalRef(clazz);
-
return AndroidRuntime::registerNativeMethods(env,
"android/mtp/MtpDevice", gMethods, NELEM(gMethods));
}
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index 8a1ae92..b4844f7 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -24,6 +24,7 @@
#include <fcntl.h>
#include <utils/threads.h>
+#include "core_jni_helpers.h"
#include "jni.h"
#include <nativehelper/JNIPlatformHelp.h>
#include "android_runtime/AndroidRuntime.h"
@@ -34,6 +35,8 @@
using namespace android;
+static Mutex sMutex;
+
// MtpServer fields
static jfieldID field_MtpServer_nativeContext;
@@ -44,7 +47,25 @@
static jfieldID field_MtpStorage_removable;
static jfieldID field_MtpStorage_maxFileSize;
-static Mutex sMutex;
+// Initializer for the jfieldIDs above. This method must be invoked before accessing MtpServer and
+// MtpStorage fields.
+static void initializeJavaIDs(JNIEnv* env) {
+ static std::once_flag sJniInitialized;
+
+ std::call_once(sJniInitialized, [](JNIEnv *env) {
+ const jclass storage_clazz = FindClassOrDie(env, "android/mtp/MtpStorage");
+ field_MtpStorage_storageId = GetFieldIDOrDie(env, storage_clazz, "mStorageId", "I");
+ field_MtpStorage_path =
+ GetFieldIDOrDie(env, storage_clazz, "mPath", "Ljava/lang/String;");
+ field_MtpStorage_description =
+ GetFieldIDOrDie(env, storage_clazz, "mDescription", "Ljava/lang/String;");
+ field_MtpStorage_removable = GetFieldIDOrDie(env, storage_clazz, "mRemovable", "Z");
+ field_MtpStorage_maxFileSize = GetFieldIDOrDie(env, storage_clazz, "mMaxFileSize", "J");
+
+ const jclass server_clazz = FindClassOrDie(env, "android/mtp/MtpServer");
+ field_MtpServer_nativeContext = GetFieldIDOrDie(env, server_clazz, "mNativeContext", "J");
+ }, env);
+}
// ----------------------------------------------------------------------------
@@ -52,6 +73,7 @@
extern IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) {
+ initializeJavaIDs(env);
return (MtpServer*)env->GetLongField(thiz, field_MtpServer_nativeContext);
}
@@ -60,6 +82,8 @@
jboolean usePtp, jstring deviceInfoManufacturer, jstring deviceInfoModel,
jstring deviceInfoDeviceVersion, jstring deviceInfoSerialNumber)
{
+ initializeJavaIDs(env);
+
const char *deviceInfoManufacturerStr = env->GetStringUTFChars(deviceInfoManufacturer, NULL);
const char *deviceInfoModelStr = env->GetStringUTFChars(deviceInfoModel, NULL);
const char *deviceInfoDeviceVersionStr = env->GetStringUTFChars(deviceInfoDeviceVersion, NULL);
@@ -224,50 +248,6 @@
int register_android_mtp_MtpServer(JNIEnv *env)
{
- jclass clazz;
-
- clazz = env->FindClass("android/mtp/MtpStorage");
- if (clazz == NULL) {
- ALOGE("Can't find android/mtp/MtpStorage");
- return -1;
- }
- field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I");
- if (field_MtpStorage_storageId == NULL) {
- ALOGE("Can't find MtpStorage.mStorageId");
- return -1;
- }
- field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;");
- if (field_MtpStorage_path == NULL) {
- ALOGE("Can't find MtpStorage.mPath");
- return -1;
- }
- field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
- if (field_MtpStorage_description == NULL) {
- ALOGE("Can't find MtpStorage.mDescription");
- return -1;
- }
- field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z");
- if (field_MtpStorage_removable == NULL) {
- ALOGE("Can't find MtpStorage.mRemovable");
- return -1;
- }
- field_MtpStorage_maxFileSize = env->GetFieldID(clazz, "mMaxFileSize", "J");
- if (field_MtpStorage_maxFileSize == NULL) {
- ALOGE("Can't find MtpStorage.mMaxFileSize");
- return -1;
- }
-
- clazz = env->FindClass("android/mtp/MtpServer");
- if (clazz == NULL) {
- ALOGE("Can't find android/mtp/MtpServer");
- return -1;
- }
- field_MtpServer_nativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
- if (field_MtpServer_nativeContext == NULL) {
- ALOGE("Can't find MtpServer.mNativeContext");
- return -1;
- }
-
return AndroidRuntime::registerNativeMethods(env,
"android/mtp/MtpServer", gMethods, NELEM(gMethods));
}
diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp
index 7b498e0..b3406cd 100644
--- a/media/jni/soundpool/Android.bp
+++ b/media/jni/soundpool/Android.bp
@@ -45,26 +45,30 @@
"modernize-return-braced-init-list",
"modernize-shrink-to-fit",
"modernize-unary-static-assert",
- "modernize-use-auto", // debatable - auto can obscure type
+ // "modernize-use-auto", // found in StreamManager.h, debatable - auto can obscure type
"modernize-use-bool-literals",
"modernize-use-default-member-init",
"modernize-use-emplace",
"modernize-use-equals-default",
"modernize-use-equals-delete",
- "modernize-use-nodiscard",
+ // "modernize-use-nodiscard", // found in SteamManager.h
"modernize-use-noexcept",
"modernize-use-nullptr",
"modernize-use-override",
//"modernize-use-trailing-return-type", // not necessarily more readable
"modernize-use-transparent-functors",
"modernize-use-uncaught-exceptions",
- "modernize-use-using",
+ //"modernize-use-using", // found in SoundManager.h
"performance-*",
// Remove some pedantic stylistic requirements.
"-google-readability-casting", // C++ casts not always necessary and may be verbose
"-google-readability-todo", // do not require TODO(info)
"-google-build-using-namespace", // Reenable and fix later.
+
+ "-google-explicit-constructor", // found in StreamManager.h
+ "-misc-non-private-member-variables-in-classes", // found in SoundManager.h
+ "-performance-unnecessary-value-param", // found in StreamManager.h
]
cc_defaults {
@@ -96,8 +100,7 @@
tidy_checks: tidy_errors,
tidy_checks_as_errors: tidy_errors,
tidy_flags: [
- "-format-style='file'",
- "--header-filter='frameworks/base/media/jni/soundpool'",
+ "-format-style=file",
],
}
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 359ef36..78cb5b0 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -216,7 +216,7 @@
Result DemuxClient::close() {
if (mTunerDemux != NULL) {
Status s = mTunerDemux->close();
- mDemux = NULL;
+ mTunerDemux = NULL;
return ClientHelper::getServiceSpecificErrorCode(s);
}
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index bbabc28..736b8f9 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -267,9 +267,6 @@
AidlMQ* mFilterMQ;
EventFlag* mFilterMQEventFlag;
- sp<FilterClientCallback> mCallback;
- sp<HidlFilterCallback> mHidlCallback;
-
native_handle_t* mAvSharedHandle;
uint64_t mAvSharedMemSize;
bool mIsMediaFilter;
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 9e36642..f54e266 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -78,8 +78,6 @@
FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type) {
mTunerFrontend = tunerFrontend;
- mAidlCallback = NULL;
- mHidlCallback = NULL;
mType = type;
}
@@ -87,22 +85,21 @@
mTunerFrontend = NULL;
mFrontend = NULL;
mFrontend_1_1 = NULL;
- mAidlCallback = NULL;
- mHidlCallback = NULL;
mId = -1;
mType = -1;
}
Result FrontendClient::setCallback(sp<FrontendClientCallback> frontendClientCallback) {
if (mTunerFrontend != NULL) {
- mAidlCallback = ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback);
- mAidlCallback->setFrontendType(mType);
- Status s = mTunerFrontend->setCallback(mAidlCallback);
+ shared_ptr<TunerFrontendCallback> aidlCallback =
+ ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback);
+ aidlCallback->setFrontendType(mType);
+ Status s = mTunerFrontend->setCallback(aidlCallback);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- mHidlCallback = new HidlFrontendCallback(frontendClientCallback);
- return mFrontend->setCallback(mHidlCallback);
+ sp<HidlFrontendCallback> hidlCallback = new HidlFrontendCallback(frontendClientCallback);
+ return mFrontend->setCallback(hidlCallback);
}
void FrontendClient::setHidlFrontend(sp<IFrontend> frontend) {
@@ -591,14 +588,15 @@
break;
}
case TunerFrontendStatus::codeRates: {
- int size = s.get<TunerFrontendStatus::codeRates>().size();
- status.codeRates().resize(size);
- for (int i = 0; i < size; i++) {
- auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i];
- status.codeRates()[i] =
- static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate);
+ vector<hardware::tv::tuner::V1_1::FrontendInnerFec> codeRates;
+ for (auto aidlCodeRate : s.get<TunerFrontendStatus::codeRates>()) {
+ codeRates.push_back(
+ static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate));
}
- hidlStatus.push_back(status);
+ if (codeRates.size() > 0) {
+ status.codeRates(codeRates);
+ hidlStatus.push_back(status);
+ }
break;
}
case TunerFrontendStatus::bandwidth: {
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index f71616c..1dd950e 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -226,9 +226,6 @@
*/
sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFrontend_1_1;
- shared_ptr<TunerFrontendCallback> mAidlCallback;
- sp<HidlFrontendCallback> mHidlCallback;
-
int mId;
int mType;
};
diff --git a/media/jni/tuner/LnbClient.cpp b/media/jni/tuner/LnbClient.cpp
index 5b6e46e..073c49a 100644
--- a/media/jni/tuner/LnbClient.cpp
+++ b/media/jni/tuner/LnbClient.cpp
@@ -45,14 +45,15 @@
Result LnbClient::setCallback(sp<LnbClientCallback> cb) {
if (mTunerLnb != NULL) {
- mAidlCallback = ::ndk::SharedRefBase::make<TunerLnbCallback>(cb);
- Status s = mTunerLnb->setCallback(mAidlCallback);
+ shared_ptr<TunerLnbCallback> aidlCallback =
+ ::ndk::SharedRefBase::make<TunerLnbCallback>(cb);
+ Status s = mTunerLnb->setCallback(aidlCallback);
return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mLnb != NULL) {
- mHidlCallback = new HidlLnbCallback(cb);
- return mLnb->setCallback(mHidlCallback);
+ sp<HidlLnbCallback> hidlCallback = new HidlLnbCallback(cb);
+ return mLnb->setCallback(hidlCallback);
}
return Result::INVALID_STATE;
diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h
index 465dc23..7c6118c 100644
--- a/media/jni/tuner/LnbClient.h
+++ b/media/jni/tuner/LnbClient.h
@@ -126,9 +126,6 @@
*/
sp<ILnb> mLnb;
- shared_ptr<TunerLnbCallback> mAidlCallback;
- sp<HidlLnbCallback> mHidlCallback;
-
LnbId mId;
};
} // namespace android
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index cf17ed6..c9a7e83 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -49,8 +49,6 @@
if (mTunerService == NULL) {
ALOGE("Failed to get tuner service");
} else {
- // TODO: b/178124017 update TRM in TunerService independently.
- mTunerService->updateTunerResources();
mTunerService->getTunerHalVersion(&mTunerVersion);
}
}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index e51add2..e8cf63f 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -27,14 +27,13 @@
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
-#include <ui/HdrCapabilities.h>
+#include <ui/DynamicDisplayInfo.h>
#include <utils/Timers.h>
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
using namespace android;
-using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
using Transaction = SurfaceComposerClient::Transaction;
@@ -72,14 +71,13 @@
return false;
}
- HdrCapabilities hdrCapabilities;
- status_t err = client->getHdrCapabilities(display, &hdrCapabilities);
- if (err) {
+ ui::DynamicDisplayInfo info;
+ if (status_t err = client->getDynamicDisplayInfo(display, &info); err != NO_ERROR) {
ALOGE("unable to get hdr capabilities");
- return false;
+ return err;
}
- return !hdrCapabilities.getSupportedHdrTypes().empty();
+ return !info.hdrCapabilities.getSupportedHdrTypes().empty();
}
static bool isDataSpaceValid(const sp<SurfaceControl>& surfaceControl, ADataSpace dataSpace) {
diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp
index 1a51218..fdca3fa 100644
--- a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp
+++ b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "UidImportanceHelperApp",
manifest: "HelperAppManifest.xml",
@@ -8,4 +17,3 @@
"general-tests",
],
}
-
diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp
index d4b5015..528ac12 100644
--- a/native/android/tests/activitymanager/nativeTests/Android.bp
+++ b/native/android/tests/activitymanager/nativeTests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test {
name: "ActivityManagerNativeTestCases",
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
index f4b46e9..eafda4d 100644
--- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
+++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
@@ -44,7 +44,7 @@
private final boolean mCaptive;
private final String mVenueFriendlyName;
private final int mVenueInfoUrlSource;
- private final int mTermsAndConditionsSource;
+ private final int mUserPortalUrlSource;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -65,7 +65,7 @@
private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
- String venueFriendlyName, int venueInfoUrlSource, int termsAndConditionsSource) {
+ String venueFriendlyName, int venueInfoUrlSource, int userPortalUrlSource) {
mRefreshTimeMillis = refreshTimeMillis;
mUserPortalUrl = userPortalUrl;
mVenueInfoUrl = venueInfoUrl;
@@ -75,7 +75,7 @@
mCaptive = captive;
mVenueFriendlyName = venueFriendlyName;
mVenueInfoUrlSource = venueInfoUrlSource;
- mTermsAndConditionsSource = termsAndConditionsSource;
+ mUserPortalUrlSource = userPortalUrlSource;
}
private CaptivePortalData(Parcel p) {
@@ -100,7 +100,7 @@
dest.writeBoolean(mCaptive);
dest.writeString(mVenueFriendlyName);
dest.writeInt(mVenueInfoUrlSource);
- dest.writeInt(mTermsAndConditionsSource);
+ dest.writeInt(mUserPortalUrlSource);
}
/**
@@ -130,7 +130,7 @@
public Builder(@Nullable CaptivePortalData data) {
if (data == null) return;
setRefreshTime(data.mRefreshTimeMillis)
- .setUserPortalUrl(data.mUserPortalUrl, data.mTermsAndConditionsSource)
+ .setUserPortalUrl(data.mUserPortalUrl, data.mUserPortalUrlSource)
.setVenueInfoUrl(data.mVenueInfoUrl, data.mVenueInfoUrlSource)
.setSessionExtendable(data.mIsSessionExtendable)
.setBytesRemaining(data.mByteLimit)
@@ -314,7 +314,7 @@
* @return The source that the user portal URL was obtained from
*/
public @CaptivePortalDataSource int getUserPortalUrlSource() {
- return mTermsAndConditionsSource;
+ return mUserPortalUrlSource;
}
/**
@@ -342,7 +342,7 @@
public int hashCode() {
return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName,
- mVenueInfoUrlSource, mTermsAndConditionsSource);
+ mVenueInfoUrlSource, mUserPortalUrlSource);
}
@Override
@@ -358,7 +358,7 @@
&& mCaptive == other.mCaptive
&& Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName)
&& mVenueInfoUrlSource == other.mVenueInfoUrlSource
- && mTermsAndConditionsSource == other.mTermsAndConditionsSource;
+ && mUserPortalUrlSource == other.mUserPortalUrlSource;
}
@Override
@@ -373,7 +373,7 @@
+ ", captive: " + mCaptive
+ ", venueFriendlyName: " + mVenueFriendlyName
+ ", venueInfoUrlSource: " + mVenueInfoUrlSource
- + ", termsAndConditionsSource: " + mTermsAndConditionsSource
+ + ", userPortalUrlSource: " + mUserPortalUrlSource
+ "}";
}
}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 4ddae53..e7ab0a1 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -1067,58 +1067,6 @@
}
/**
- * Calls VpnManager#isAlwaysOnVpnPackageSupportedForUser.
- * @deprecated TODO: remove when callers have migrated to VpnManager.
- * @hide
- */
- @Deprecated
- public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
- return getVpnManager().isAlwaysOnVpnPackageSupportedForUser(userId, vpnPackage);
- }
-
- /**
- * Calls VpnManager#setAlwaysOnVpnPackageForUser.
- * @deprecated TODO: remove when callers have migrated to VpnManager.
- * @hide
- */
- @Deprecated
- public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
- boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) {
- return getVpnManager().setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdownEnabled,
- lockdownAllowlist);
- }
-
- /**
- * Calls VpnManager#getAlwaysOnVpnPackageForUser.
- * @deprecated TODO: remove when callers have migrated to VpnManager.
- * @hide
- */
- @Deprecated
- public String getAlwaysOnVpnPackageForUser(int userId) {
- return getVpnManager().getAlwaysOnVpnPackageForUser(userId);
- }
-
- /**
- * Calls VpnManager#isVpnLockdownEnabled.
- * @deprecated TODO: remove when callers have migrated to VpnManager.
- * @hide
- */
- @Deprecated
- public boolean isVpnLockdownEnabled(int userId) {
- return getVpnManager().isVpnLockdownEnabled(userId);
- }
-
- /**
- * Calls VpnManager#getVpnLockdownAllowlist.
- * @deprecated TODO: remove when callers have migrated to VpnManager.
- * @hide
- */
- @Deprecated
- public List<String> getVpnLockdownAllowlist(int userId) {
- return getVpnManager().getVpnLockdownAllowlist(userId);
- }
-
- /**
* Adds or removes a requirement for given UID ranges to use the VPN.
*
* If set to {@code true}, informs the system that the UIDs in the specified ranges must not
@@ -3153,16 +3101,6 @@
}
/**
- * Calls VpnManager#updateLockdownVpn.
- * @deprecated TODO: remove when callers have migrated to VpnManager.
- * @hide
- */
- @Deprecated
- public boolean updateLockdownVpn() {
- return getVpnManager().updateLockdownVpn();
- }
-
- /**
* Set sign in error notification to visible or invisible
*
* @hide
@@ -4523,8 +4461,6 @@
try {
mService.factoryReset();
mTetheringManager.stopAllTethering();
- // TODO: Migrate callers to VpnManager#factoryReset.
- getVpnManager().factoryReset();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4818,15 +4754,6 @@
return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder));
}
- /**
- * Temporary hack to shim calls from ConnectivityManager to VpnManager. We cannot store a
- * private final mVpnManager because ConnectivityManager is initialized before VpnManager.
- * @hide TODO: remove.
- */
- public VpnManager getVpnManager() {
- return mContext.getSystemService(VpnManager.class);
- }
-
/** @hide */
public ConnectivityDiagnosticsManager createDiagnosticsManager() {
return new ConnectivityDiagnosticsManager(mContext, mService);
diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java
index bcb65fa..d2ee7d1 100644
--- a/packages/Connectivity/framework/src/android/net/IpPrefix.java
+++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java
@@ -24,6 +24,8 @@
import android.os.Parcelable;
import android.util.Pair;
+import com.android.net.module.util.NetUtils;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -59,7 +61,7 @@
throw new IllegalArgumentException(
"IpPrefix has " + address.length + " bytes which is neither 4 nor 16");
}
- NetworkUtils.maskRawAddress(address, prefixLength);
+ NetUtils.maskRawAddress(address, prefixLength);
}
/**
@@ -190,7 +192,7 @@
if (addrBytes == null || addrBytes.length != this.address.length) {
return false;
}
- NetworkUtils.maskRawAddress(addrBytes, prefixLength);
+ NetUtils.maskRawAddress(addrBytes, prefixLength);
return Arrays.equals(this.address, addrBytes);
}
@@ -204,7 +206,7 @@
public boolean containsPrefix(@NonNull IpPrefix otherPrefix) {
if (otherPrefix.getPrefixLength() < prefixLength) return false;
final byte[] otherAddress = otherPrefix.getRawAddress();
- NetworkUtils.maskRawAddress(otherAddress, prefixLength);
+ NetUtils.maskRawAddress(otherAddress, prefixLength);
return Arrays.equals(otherAddress, address);
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 26d14cb..cd76f40 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -205,6 +205,7 @@
NET_CAPABILITY_OEM_PRIVATE,
NET_CAPABILITY_VEHICLE_INTERNAL,
NET_CAPABILITY_NOT_VCN_MANAGED,
+ NET_CAPABILITY_ENTERPRISE,
})
public @interface NetCapability { }
@@ -415,8 +416,17 @@
@SystemApi
public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
+ /**
+ * Indicates that this network is intended for enterprise use.
+ * <p>
+ * 5G URSP rules may indicate that all data should use a connection dedicated for enterprise
+ * use. If the enterprise capability is requested, all enterprise traffic will be routed over
+ * the connection with this capability.
+ */
+ public static final int NET_CAPABILITY_ENTERPRISE = 29;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_ENTERPRISE;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -474,7 +484,8 @@
| (1 << NET_CAPABILITY_MCX)
| (1 << NET_CAPABILITY_RCS)
| (1 << NET_CAPABILITY_VEHICLE_INTERNAL)
- | (1 << NET_CAPABILITY_XCAP);
+ | (1 << NET_CAPABILITY_XCAP)
+ | (1 << NET_CAPABILITY_ENTERPRISE);
/**
* Capabilities that force network to be restricted.
@@ -2028,8 +2039,9 @@
case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED";
case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE";
- case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL";
+ case NET_CAPABILITY_VEHICLE_INTERNAL: return "VEHICLE_INTERNAL";
case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED";
+ case NET_CAPABILITY_ENTERPRISE: return "ENTERPRISE";
default: return Integer.toString(capability);
}
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index 8be4af7..9ccb04a 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -29,7 +29,6 @@
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.SocketException;
-import java.net.UnknownHostException;
import java.util.Locale;
import java.util.TreeSet;
@@ -232,46 +231,6 @@
}
/**
- * Masks a raw IP address byte array with the specified prefix length.
- */
- public static void maskRawAddress(byte[] array, int prefixLength) {
- if (prefixLength < 0 || prefixLength > array.length * 8) {
- throw new RuntimeException("IP address with " + array.length +
- " bytes has invalid prefix length " + prefixLength);
- }
-
- int offset = prefixLength / 8;
- int remainder = prefixLength % 8;
- byte mask = (byte)(0xFF << (8 - remainder));
-
- if (offset < array.length) array[offset] = (byte)(array[offset] & mask);
-
- offset++;
-
- for (; offset < array.length; offset++) {
- array[offset] = 0;
- }
- }
-
- /**
- * Get InetAddress masked with prefixLength. Will never return null.
- * @param address the IP address to mask with
- * @param prefixLength the prefixLength used to mask the IP
- */
- public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
- byte[] array = address.getAddress();
- maskRawAddress(array, prefixLength);
-
- InetAddress netPart = null;
- try {
- netPart = InetAddress.getByAddress(array);
- } catch (UnknownHostException e) {
- throw new RuntimeException("getNetworkPart error - " + e.toString());
- }
- return netPart;
- }
-
- /**
* Returns the implicit netmask of an IPv4 address, as was the custom before 1993.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/packages/DynamicSystemInstallationService/res/values-eu/strings.xml b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml
index 6576edd..b0d45e1d0 100644
--- a/packages/DynamicSystemInstallationService/res/values-eu/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml
@@ -5,7 +5,7 @@
<string name="notification_install_completed" msgid="6252047868415172643">"Prest dago sistema dinamikoa. Erabiltzen hasteko, berrabiarazi gailua."</string>
<string name="notification_install_inprogress" msgid="7383334330065065017">"Instalatzen"</string>
<string name="notification_install_failed" msgid="4066039210317521404">"Ezin izan da instalatu"</string>
- <string name="notification_image_validation_failed" msgid="2720357826403917016">"Ezin izan da balidatu irudia. Utzi bertan behera instalazioa."</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Ezin izan da baliozkotu irudia. Utzi bertan behera instalazioa."</string>
<string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Sistema dinamiko bat abian da. Berrabiarazi Android-en jatorrizko bertsioa erabiltzeko."</string>
<string name="notification_action_cancel" msgid="5929299408545961077">"Utzi"</string>
<string name="notification_action_discard" msgid="1817481003134947493">"Baztertu"</string>
diff --git a/packages/InputDevices/OWNERS b/packages/InputDevices/OWNERS
index 0313a40..f0d6db8 100644
--- a/packages/InputDevices/OWNERS
+++ b/packages/InputDevices/OWNERS
@@ -1,2 +1 @@
-michaelwr@google.com
-svv@google.com
+include /services/core/java/com/android/server/input/OWNERS
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index e8ad16b..f2a0e1c 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -66,6 +66,7 @@
"SettingsLibBannerMessagePreference",
"SettingsLibFooterPreference",
"SettingsLibUsageProgressBarPreference",
+ "SettingsLibCollapsingToolbarBaseActivity",
],
}
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index 095975a..82e837b 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibBannerMessagePreference",
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
new file mode 100644
index 0000000..ed49bf4
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -0,0 +1,23 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SettingsLibCollapsingToolbarBaseActivity",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.core_core",
+ "com.google.android.material_material",
+ ],
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml
new file mode 100644
index 0000000..dabba68
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/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.collapsingtoolbar">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml
new file mode 100644
index 0000000..e376930
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml
@@ -0,0 +1,60 @@
+<?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.
+-->
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/content_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:transitionGroup="true">
+
+ <com.google.android.material.appbar.AppBarLayout
+ android:id="@+id/app_bar"
+ android:layout_width="match_parent"
+ android:layout_height="180dp"
+ android:theme="@style/Theme.CollapsingToolbar.Settings">
+
+ <com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout
+ android:id="@+id/collapsing_toolbar"
+ android:background="?android:attr/colorPrimary"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:maxLines="3"
+ app:contentScrim="?android:attr/colorPrimary"
+ app:collapsedTitleTextAppearance="@style/CollapsingToolbarTitle.Collapsed"
+ app:statusBarScrim="?android:attr/colorPrimary"
+ app:layout_scrollFlags="scroll|exitUntilCollapsed"
+ app:expandedTitleMarginStart="18dp"
+ app:expandedTitleMarginEnd="18dp"
+ app:toolbarId="@id/action_bar">
+
+ <Toolbar
+ android:id="@+id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:theme="?android:attr/actionBarTheme"
+ app:layout_collapseMode="pin"/>
+
+ </com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout>
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
new file mode 100644
index 0000000..e20775e
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.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.
+-->
+<resources>
+ <style name="Theme.CollapsingToolbar.Settings"
+ parent="@style/Theme.MaterialComponents.DayNight">
+ <item name="colorPrimary">@*android:color/primary_dark_device_default_settings</item>
+ <item name="colorAccent">@*android:color/accent_device_default_dark</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
new file mode 100644
index 0000000..e1a64d4
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?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>
+ <style name="CollapsingToolbarTitle.Collapsed"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ </style>
+
+ <style name="CollapsingToolbarTitle"
+ parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">36sp</item>
+ </style>
+
+ <style name="CollapsingToolbarTitle.MoreThanTwoLines">
+ <item name="android:textSize">24sp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml
new file mode 100644
index 0000000..de545b0
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.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.
+-->
+<resources>
+ <style name="Theme.CollapsingToolbar.Settings"
+ parent="@style/Theme.MaterialComponents.DayNight">
+ <item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
+ <item name="colorAccent">@*android:color/accent_device_default_light</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java
new file mode 100644
index 0000000..e75a978
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java
@@ -0,0 +1,92 @@
+/*
+ * 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.settingslib.collapsingtoolbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.material.appbar.CollapsingToolbarLayout;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * A customized version of CollapsingToolbarLayout that can apply different font size based on
+ * the line count of its title.
+ */
+public class AdjustableToolbarLayout extends CollapsingToolbarLayout {
+
+ private static final int TOOLBAR_MAX_LINE_NUMBER = 2;
+
+ public AdjustableToolbarLayout(@NonNull Context context) {
+ this(context, null);
+
+ }
+
+ public AdjustableToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AdjustableToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initCollapsingToolbar();
+ }
+
+ private void initCollapsingToolbar() {
+ this.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ v.removeOnLayoutChangeListener(this);
+ final int count = getLineCount();
+ if (count > TOOLBAR_MAX_LINE_NUMBER) {
+ setExpandedTitleTextAppearance(R.style.CollapsingToolbarTitle_MoreThanTwoLines);
+ } else {
+ setExpandedTitleTextAppearance(R.style.CollapsingToolbarTitle);
+ }
+ }
+ });
+ }
+
+ /**
+ * Returns a number of title line count for CollapsingToolbarLayout so that facilitates the
+ * determination to apply what kind of font size. Since the title of CollapsingToolbarLayout is
+ * drawn in a canvas and the text process is wrapped in a CollapsingTextHelper, the way we used
+ * here is to get the line count from the CollapsingTextHelper via Java Reflection.
+ */
+ private int getLineCount() {
+ try {
+ final Field textHelperField = this.getClass().getDeclaredField("collapsingTextHelper");
+ textHelperField.setAccessible(true);
+ final Object textHelperObj = textHelperField.get(this);
+
+ final Field layoutField = textHelperObj.getClass().getDeclaredField("textLayout");
+ layoutField.setAccessible(true);
+ final Object layoutObj = layoutField.get(textHelperObj);
+
+ final Method method = layoutObj.getClass().getDeclaredMethod("getLineCount");
+ return (int) method.invoke(layoutObj);
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
new file mode 100644
index 0000000..637805f
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -0,0 +1,99 @@
+/*
+ * 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.settingslib.collapsingtoolbar;
+
+import android.app.ActionBar;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toolbar;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+
+import com.google.android.material.appbar.CollapsingToolbarLayout;
+
+/**
+ * A base Activity that has a collapsing toolbar layout is used for the activities intending to
+ * enable the collapsing toolbar function.
+ */
+public class CollapsingToolbarBaseActivity extends FragmentActivity {
+
+ private CollapsingToolbarLayout mCollapsingToolbarLayout;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ super.setContentView(R.layout.collapsing_toolbar_base_layout);
+ mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
+
+ final Toolbar toolbar = findViewById(R.id.action_bar);
+ setActionBar(toolbar);
+
+ // Enable title and home button by default
+ final ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setHomeButtonEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
+ }
+ }
+
+ @Override
+ public void setContentView(int layoutResID) {
+ final ViewGroup parent = findViewById(R.id.content_frame);
+ if (parent != null) {
+ parent.removeAllViews();
+ }
+ LayoutInflater.from(this).inflate(layoutResID, parent);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ ((ViewGroup) findViewById(R.id.content_frame)).addView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ ((ViewGroup) findViewById(R.id.content_frame)).addView(view, params);
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ if (mCollapsingToolbarLayout != null) {
+ mCollapsingToolbarLayout.setTitle(title);
+ }
+ super.setTitle(title);
+ }
+
+ @Override
+ public void setTitle(int titleId) {
+ if (mCollapsingToolbarLayout != null) {
+ mCollapsingToolbarLayout.setTitle(getText(titleId));
+ }
+ super.setTitle(titleId);
+ }
+
+ /**
+ * Returns an instance of collapsing toolbar.
+ */
+ public CollapsingToolbarLayout getCollapsingToolbarLayout() {
+ return mCollapsingToolbarLayout;
+ }
+}
diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp
index 3c41f78..25b4905 100644
--- a/packages/SettingsLib/EmergencyNumber/Android.bp
+++ b/packages/SettingsLib/EmergencyNumber/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibEmergencyNumber",
diff --git a/packages/SettingsLib/FooterPreference/Android.bp b/packages/SettingsLib/FooterPreference/Android.bp
index 1af967f..11f39e7 100644
--- a/packages/SettingsLib/FooterPreference/Android.bp
+++ b/packages/SettingsLib/FooterPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibFooterPreference",
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 1dc18f5..1feec21 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibMainSwitchPreference",
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
index 52779bc..85c01c5 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
@@ -19,6 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
+ android:background="?android:attr/colorBackground"
android:orientation="vertical">
<LinearLayout
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 74b6578..1c9298e 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -17,6 +17,7 @@
package com.android.settingslib.widget;
import android.content.Context;
+import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -29,6 +30,7 @@
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
+import androidx.core.content.res.TypedArrayUtils;
import com.android.settingslib.RestrictedLockUtils;
@@ -88,6 +90,17 @@
});
setChecked(mSwitch.isChecked());
+
+ if (attrs != null) {
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
+ 0 /*defStyleRes*/);
+ final CharSequence title = TypedArrayUtils.getText(a,
+ androidx.preference.R.styleable.Preference_title,
+ androidx.preference.R.styleable.Preference_android_title);
+ setTitle(title);
+ a.recycle();
+ }
}
@Override
@@ -126,7 +139,7 @@
/**
* Set the title text
*/
- public void setTitle(String text) {
+ public void setTitle(CharSequence text) {
if (mTextView != null) {
mTextView.setText(text);
}
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index 274bf8d..35afec3 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.content.res.TypedArray;
-import android.text.TextUtils;
import android.util.AttributeSet;
import androidx.core.content.res.TypedArrayUtils;
@@ -40,7 +39,7 @@
private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>();
private MainSwitchBar mMainSwitchBar;
- private String mTitle;
+ private CharSequence mTitle;
private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin;
@@ -81,24 +80,28 @@
setLayoutResource(R.layout.main_switch_layout);
if (attrs != null) {
- TypedArray a = context.obtainStyledAttributes(attrs,
+ final TypedArray a = context.obtainStyledAttributes(attrs,
androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
0 /*defStyleRes*/);
final CharSequence title = TypedArrayUtils.getText(a,
androidx.preference.R.styleable.Preference_title,
androidx.preference.R.styleable.Preference_android_title);
- if (!TextUtils.isEmpty(title)) {
- setTitle(title.toString());
- }
+ setTitle(title);
a.recycle();
}
}
- /**
- * Set the preference title text
- */
- public void setTitle(String text) {
- mTitle = text;
+ @Override
+ public void setChecked(boolean checked) {
+ super.setChecked(checked);
+ if (mMainSwitchBar != null) {
+ mMainSwitchBar.setChecked(checked);
+ }
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mTitle = title;
if (mMainSwitchBar != null) {
mMainSwitchBar.setTitle(mTitle);
}
diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp
index 03becbd..9577281 100644
--- a/packages/SettingsLib/TopIntroPreference/Android.bp
+++ b/packages/SettingsLib/TopIntroPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibTopIntroPreference",
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index 19fa91f..0287b1f 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -20,27 +20,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingStart="20dp"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:clipToPadding="false">
- <LinearLayout
- android:id="@+id/icon_frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:minWidth="56dp"
- android:gravity="start|top"
- android:orientation="horizontal"
- android:paddingEnd="12dp"
- android:paddingTop="16dp"
- android:paddingBottom="4dp">
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout>
-
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
@@ -50,5 +34,5 @@
android:clickable="false"
android:longClickable="false"
android:maxLines="10"
- android:textColor="?android:attr/textColorSecondary"/>
+ android:textAppearance="@style/TextAppearance.TopIntroText"/>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
new file mode 100644
index 0000000..e7eb9f4
--- /dev/null
+++ b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
@@ -0,0 +1,24 @@
+<?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>
+ <style name="TextAppearance.TopIntroText"
+ parent="@*android:style/TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+</resources>
diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
index 3331550..ad6e7ab 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/Android.bp
+++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibUsageProgressBarPreference",
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 3866151..79fbcc3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1007,6 +1007,9 @@
<!-- Settings item title to select the default behavior for transcoding if an encodig is not supported by an app. [CHAR LIMIT=85] -->
<string name="transcode_default">Assume apps support modern formats</string>
+ <!-- Settings item title to select whether to show transcoding notifications. [CHAR LIMIT=85] -->
+ <string name="transcode_notification">Show transcoding notifications</string>
+
<!-- Services settings screen, setting option name for the user to go to the screen to view running services -->
<string name="runningservices_settings_title">Running services</string>
<!-- Services settings screen, setting option summary for the user to go to the screen to view running services -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4c80b91..5e2d21b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -851,11 +851,12 @@
if (BluetoothUuid.containsAnyUuid(uuids, PbapServerProfile.PBAB_CLIENT_UUIDS)) {
// The pairing dialog now warns of phone-book access for paired devices.
// No separate prompt is displayed after pairing.
+ final BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
- if (mDevice.getBluetoothClass().getDeviceClass()
- == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE ||
- mDevice.getBluetoothClass().getDeviceClass()
- == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET) {
+ if (bluetoothClass != null && (bluetoothClass.getDeviceClass()
+ == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE
+ || bluetoothClass.getDeviceClass()
+ == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
EventLog.writeEvent(0x534e4554, "138529441", -1, "");
}
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 32419f4..1f3e0e9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -205,7 +205,6 @@
void dispatchDeviceListUpdate() {
final List<MediaDevice> mediaDevices = new ArrayList<>(mMediaDevices);
- Collections.sort(mediaDevices, COMPARATOR);
for (DeviceCallback callback : getCallbacks()) {
callback.onDeviceListUpdate(mediaDevices);
}
@@ -472,6 +471,7 @@
synchronized (mMediaDevicesLock) {
mMediaDevices.clear();
mMediaDevices.addAll(devices);
+ Collections.sort(devices, COMPARATOR);
// Add disconnected bluetooth devices only when phone output device is available.
for (MediaDevice device : devices) {
final int type = device.getDeviceType();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
index 647fd2a..552fa11 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
@@ -39,8 +39,7 @@
/**
* A string extra specifying a media package name.
*/
- public static final String EXTRA_PACKAGE_NAME =
- "com.android.settings.panel.extra.PACKAGE_NAME";
+ public static final String EXTRA_PACKAGE_NAME = "package_name";
/**
* An intent action to launch media output dialog.
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 53ff1a1..53a99ab 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -27,12 +27,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.AudioManager;
import com.android.settingslib.R;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
@@ -41,8 +43,11 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
public class CachedBluetoothDeviceTest {
private static final String DEVICE_NAME = "TestName";
private static final String DEVICE_ALIAS = "TestAlias";
@@ -72,12 +77,14 @@
private AudioManager mAudioManager;
private Context mContext;
private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mAudioManager = mContext.getSystemService(AudioManager.class);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
@@ -937,4 +944,17 @@
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
mContext.getString(R.string.profile_connect_timeout_subtext));
}
+
+ @Test
+ public void onUuidChanged_bluetoothClassIsNull_shouldNotCrash() {
+ mShadowBluetoothAdapter.setUuids(PbapServerProfile.PBAB_CLIENT_UUIDS);
+ when(mDevice.getUuids()).thenReturn(PbapServerProfile.PBAB_CLIENT_UUIDS);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice.getPhonebookAccessPermission()).thenReturn(BluetoothDevice.ACCESS_UNKNOWN);
+ when(mDevice.getBluetoothClass()).thenReturn(null);
+
+ mCachedDevice.onUuidChanged();
+
+ // Should not crash
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index b265d46..3b7fbc7 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -24,6 +24,7 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.os.ParcelUuid;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -36,6 +37,7 @@
private List<Integer> mSupportedProfiles;
private List<BluetoothDevice> mMostRecentlyConnectedDevices;
private BluetoothProfile.ServiceListener mServiceListener;
+ private ParcelUuid[] mParcelUuids;
@Implementation
protected boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
@@ -87,4 +89,13 @@
}
return true;
}
+
+ @Implementation
+ protected ParcelUuid[] getUuids() {
+ return mParcelUuids;
+ }
+
+ public void setUuids(ParcelUuid[] uuids) {
+ mParcelUuids = uuids;
+ }
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 6719f17..664fd4a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -298,7 +298,6 @@
Settings.Global.GNSS_SATELLITE_BLOCKLIST,
Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
Settings.Global.HDMI_CEC_SWITCH_ENABLED,
- Settings.Global.HDMI_CEC_VERSION,
Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
Settings.Global.HDMI_CONTROL_ENABLED,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a15ceb6..2594840 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -86,6 +86,7 @@
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<!-- TODO(b/152310230): remove once APIs are confirmed to be sufficient -->
<uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
+ <uses-permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" />
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
<uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 34901f5..6d738f8 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -9,3 +9,4 @@
toddke@google.com
cbrubaker@google.com
omakoto@google.com
+michaelwr@google.com
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2faca8d..fbe58c5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -334,6 +334,11 @@
</intent-filter>
</receiver>
+ <activity android:name=".screenshot.LongScreenshotActivity"
+ android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
+ android:process=":screenshot"
+ android:finishOnTaskLaunch="true" />
+
<activity android:name=".screenrecord.ScreenRecordDialog"
android:theme="@style/ScreenRecord"
android:showForAllUsers="true"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index f3ec39d..b6a41c2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -64,7 +64,7 @@
android:id="@+id/new_lockscreen_clock_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:visibility="gone">
<com.android.keyguard.AnimatableClockView
@@ -73,7 +73,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
- android:textSize="100dp"
+ android:textSize="60dp"
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 2a5784a..115a156 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -47,26 +47,25 @@
<dimen name="eca_overlap">-10dip</dimen>
<!-- Slice header -->
- <dimen name="widget_title_font_size">24dp</dimen>
- <dimen name="header_subtitle_padding">12dp</dimen>
+ <dimen name="widget_title_font_size">20dp</dimen>
+ <dimen name="widget_title_line_height">24dp</dimen>
<dimen name="header_icon_size">16dp</dimen>
<!-- Slice subtitle -->
- <dimen name="widget_label_font_size">18dp</dimen>
+ <dimen name="widget_label_font_size">16dp</dimen>
+ <dimen name="widget_label_line_height">20dp</dimen>
<!-- Clock without header -->
<dimen name="widget_big_font_size">54dp</dimen>
<dimen name="bottom_text_spacing_digital">0dp</dimen>
<dimen name="title_clock_padding">4dp</dimen>
<!-- Clock with header -->
<dimen name="widget_small_font_size">@dimen/widget_title_font_size</dimen>
- <dimen name="widget_vertical_padding">17dp</dimen>
+ <dimen name="widget_vertical_padding">5dp</dimen>
<dimen name="widget_vertical_padding_with_header">25dp</dimen>
<dimen name="widget_vertical_padding_clock">12dp</dimen>
<!-- Subtitle paddings -->
<dimen name="widget_horizontal_padding">8dp</dimen>
- <dimen name="widget_icon_size">20dp</dimen>
+ <dimen name="widget_icon_size">18dp</dimen>
<dimen name="widget_icon_padding">8dp</dimen>
- <dimen name="subtitle_clock_padding">0dp</dimen>
- <dimen name="header_row_font_size">14dp</dimen>
<!-- Notification shelf padding when dark -->
<dimen name="widget_bottom_separator_padding">-6dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 2391803..8f42cbe 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -120,6 +120,7 @@
<style name="TextAppearance.Keyguard">
<item name="android:textSize">@dimen/widget_title_font_size</item>
+ <item name="android:lineHeight">@dimen/widget_title_line_height</item>
<item name="android:gravity">center</item>
<item name="android:ellipsize">end</item>
<item name="android:maxLines">2</item>
@@ -133,6 +134,7 @@
<item name="android:layout_height">wrap_content</item>
<item name="android:lines">1</item>
<item name="android:textSize">@dimen/widget_label_font_size</item>
+ <item name="android:lineHeight">@dimen/widget_label_line_height</item>
</style>
<style name="TextAppearance.Keyguard.BottomArea">
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
index d097472..8efe053 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
@@ -41,8 +41,10 @@
</item>
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
- <com.android.systemui.util.RoundedCornerProgressDrawable
+ <clip
android:drawable="@drawable/brightness_progress_full_drawable"
+ android:clipOrientation="horizontal"
+ android:gravity="left"
/>
</item>
</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
index 41140a7..5bc2773 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
@@ -26,10 +26,10 @@
</item>
<item
android:id="@+id/slider_icon"
- android:gravity="center_vertical|right"
+ android:gravity="center_vertical|left"
android:height="@dimen/rounded_slider_icon_size"
android:width="@dimen/rounded_slider_icon_size"
- android:right="@dimen/rounded_slider_icon_inset">
+ android:left="@dimen/rounded_slider_icon_inset">
<com.android.systemui.util.AlphaTintDrawableWrapper
android:drawable="@drawable/ic_brightness"
android:tint="?android:attr/colorBackground"
diff --git a/packages/SystemUI/res/drawable/ic_phone_missed.xml b/packages/SystemUI/res/drawable/ic_phone_missed.xml
new file mode 100644
index 0000000..72e67d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_phone_missed.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"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.5,5.5L12,11l7,-7 -1,-1 -6,6 -4.5,-4.5L11,4.5L11,3L5,3v6h1.5L6.5,5.5zM23.71,16.67C20.66,13.78 16.54,12 12,12 7.46,12 3.34,13.78 0.29,16.67c-0.18,0.18 -0.29,0.43 -0.29,0.71s0.11,0.53 0.29,0.71l2.48,2.48c0.18,0.18 0.43,0.29 0.71,0.29 0.27,0 0.52,-0.11 0.7,-0.28 0.79,-0.74 1.69,-1.36 2.66,-1.85 0.33,-0.16 0.56,-0.5 0.56,-0.9v-3.1c1.45,-0.48 3,-0.73 4.6,-0.73 1.6,0 3.15,0.25 4.6,0.72v3.1c0,0.39 0.23,0.74 0.56,0.9 0.98,0.49 1.87,1.12 2.67,1.85 0.18,0.18 0.43,0.28 0.7,0.28 0.28,0 0.53,-0.11 0.71,-0.29l2.48,-2.48c0.18,-0.18 0.29,-0.43 0.29,-0.71s-0.12,-0.52 -0.3,-0.7z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/people_space_content_background.xml b/packages/SystemUI/res/drawable/people_space_content_background.xml
index 32314d2..30519ae 100644
--- a/packages/SystemUI/res/drawable/people_space_content_background.xml
+++ b/packages/SystemUI/res/drawable/people_space_content_background.xml
@@ -15,6 +15,6 @@
~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
- <solid android:color="?android:attr/colorControlHighlight" />
+ <solid android:color="?android:attr/colorBackground" />
<corners android:radius="@dimen/people_space_image_radius" />
</shape>
diff --git a/packages/SystemUI/res/drawable/toast_background.xml b/packages/SystemUI/res/drawable/toast_background.xml
deleted file mode 100644
index 5c45e83..0000000
--- a/packages/SystemUI/res/drawable/toast_background.xml
+++ /dev/null
@@ -1,21 +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.
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="#FFFFFFFF" />
- <corners android:radius="@dimen/toast_bg_radius" />
-</shape>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 73beefc..d996cee 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -32,7 +32,8 @@
android:id="@+id/header_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"/>
+ android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"
+ android:importantForAccessibility="no"/>
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index a8ef7c3..4ca59f5 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -37,12 +37,6 @@
android:layout_height="match_parent"
android:layout_width="match_parent" />
- <ViewStub
- android:id="@+id/keyguard_user_switcher_stub"
- android:layout="@layout/keyguard_user_switcher"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
-
<include
layout="@layout/keyguard_status_view"
android:visibility="gone" />
@@ -73,7 +67,7 @@
android:id="@+id/qs_edge_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- systemui:layout_constraintGuide_percent="0.5"
+ systemui:layout_constraintGuide_percent="0.4"
android:orientation="vertical"/>
<com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -109,5 +103,11 @@
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
+ <ViewStub
+ android:id="@+id/keyguard_user_switcher_stub"
+ android:layout="@layout/keyguard_user_switcher"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+
<include layout="@layout/status_bar_expanded_plugin_frame"/>
</com.android.systemui.statusbar.phone.NotificationPanelView>
diff --git a/packages/SystemUI/res/layout/text_toast.xml b/packages/SystemUI/res/layout/text_toast.xml
index de4e062..ad558d8 100644
--- a/packages/SystemUI/res/layout/text_toast.xml
+++ b/packages/SystemUI/res/layout/text_toast.xml
@@ -20,32 +20,30 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:maxWidth="@dimen/toast_width"
android:orientation="horizontal"
- android:background="@drawable/toast_background"
- android:backgroundTint="?android:attr/colorBackground"
+ android:gravity="center_vertical"
+ android:maxWidth="@*android:dimen/toast_width"
+ android:background="@android:drawable/toast_frame"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
- android:gravity="center_vertical">
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp">
- <!-- Icon should be 24x24, make slightly larger to allow for shadowing, adjust via padding -->
<ImageView
android:id="@+id/icon"
- android:alpha="@dimen/toast_icon_alpha"
- android:padding="11.5dp"
- android:layout_width="@dimen/toast_icon_size"
- android:layout_height="@dimen/toast_icon_size"/>
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:layout_marginEnd="10dp"/>
<TextView
android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:paddingTop="12dp"
android:paddingBottom="12dp"
- android:paddingStart="0dp"
- android:paddingEnd="22dp"
- android:textSize="@dimen/toast_text_size"
- android:textColor="?android:attr/textColorPrimary"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ android:lineHeight="20sp"
+ android:textAppearance="@*android:style/TextAppearance.Toast"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
deleted file mode 100644
index b62018d..0000000
--- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="12dp">
-
- <FrameLayout
- android:layout_width="34dp"
- android:layout_height="24dp"
- android:layout_gravity="center"
- android:background="@drawable/tv_rect_shadow_rounded">
-
- <ImageView
- android:layout_width="13dp"
- android:layout_height="13dp"
- android:layout_gravity="center"
- android:src="@drawable/tv_ic_mic_white"/>
-
- </FrameLayout>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
new file mode 100644
index 0000000..dff148b
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="12dp"
+ android:layout_gravity="center">
+
+ <LinearLayout
+ android:id="@+id/icons_container"
+ android:background="@drawable/tv_rect_shadow_rounded"
+ android:padding="@dimen/privacy_chip_icon_padding"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"/>
+
+</FrameLayout>
+
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 24c7655..51d7b8e 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -52,6 +52,4 @@
<!-- (footer_height -48dp)/2 -->
<dimen name="controls_management_footer_top_margin">4dp</dimen>
<dimen name="controls_management_favorites_top_margin">8dp</dimen>
-
- <dimen name="toast_y_offset">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 722f148..b02d8b8 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -28,6 +28,7 @@
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.volume.VolumeUI</item>
+ <item>com.android.systemui.privacy.television.TvOngoingPrivacyChip</item>
<item>com.android.systemui.statusbar.tv.TvStatusBar</item>
<item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item>
<item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item>
diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml
index 6da0c69..7626db9 100644
--- a/packages/SystemUI/res/values-television/dimens.xml
+++ b/packages/SystemUI/res/values-television/dimens.xml
@@ -17,4 +17,8 @@
<resources>
<!-- Opacity at which the background for the shutdown UI will be drawn. -->
<item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item>
+
+ <dimen name="privacy_chip_icon_margin">3dp</dimen>
+ <dimen name="privacy_chip_icon_padding">8dp</dimen>
+ <dimen name="privacy_chip_icon_size">13dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6e458681..be49e1f 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -170,7 +170,6 @@
<declare-styleable name="AlphaTintDrawableWrapper">
<attr name="android:tint" />
- <attr name="android:drawable" />
<attr name="android:alpha" />
</declare-styleable>
@@ -191,9 +190,5 @@
<attr name="borderThickness" format="dimension" />
<attr name="borderColor" format="color" />
</declare-styleable>
-
- <declare-styleable name="RoundedCornerProgressDrawable">
- <attr name="android:drawable" />
- </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 13c0110..9fc0e92 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -574,4 +574,7 @@
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">false</bool>
+
+ <!-- Determines whether the shell features all run on another thread. -->
+ <bool name="config_enableShellMainThread">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
deleted file mode 100644
index 2e77674..0000000
--- a/packages/SystemUI/res/values/config_tv.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <!-- Whether to enable microphone disclosure indicator
- (com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar). -->
- <bool name="audio_recording_disclosure_enabled">true</bool>
-</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b07df9c..89c849a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -651,7 +651,7 @@
</dimen>
<!-- The height of a notification header -->
- <dimen name="notification_header_height">53dp</dimen>
+ <dimen name="notification_header_height">@*android:dimen/notification_header_height</dimen>
<!-- The height of the gap between adjacent notification sections. -->
<dimen name="notification_section_divider_height">@dimen/notification_side_paddings</dimen>
@@ -1359,11 +1359,4 @@
<dimen name="rounded_slider_icon_size">24dp</dimen>
<!-- rounded_slider_icon_size / 2 -->
<dimen name="rounded_slider_icon_inset">12dp</dimen>
-
- <dimen name="toast_width">296dp</dimen>
- <item name="toast_icon_alpha" format="float" type="dimen">1</item>
- <dimen name="toast_text_size">14sp</dimen>
- <dimen name="toast_y_offset">48dp</dimen>
- <dimen name="toast_icon_size">48dp</dimen>
- <dimen name="toast_bg_radius">28dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index d4bb128..3e16cd4 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -42,4 +42,6 @@
<bool name="flag_lockscreen_animations">false</bool>
<bool name="flag_toast_style">false</bool>
+
+ <bool name="flag_navigation_bar_overlay">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3b42600..4b95c16 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2717,6 +2717,9 @@
<!-- Controls dialog confirmation [CHAR LIMIT=30] -->
<string name="controls_dialog_confirmation">Controls updated</string>
+ <!-- Controls tile secondary label when device is locked and user does not want access to controls from lockscreen [CHAR LIMIT=20] -->
+ <string name="controls_tile_locked">Device locked</string>
+
<!-- Controls PIN entry dialog, switch to alphanumeric keyboard [CHAR LIMIT=100] -->
<string name="controls_pin_use_alphanumeric">PIN contains letters or symbols</string>
<!-- Controls PIN entry dialog, title [CHAR LIMIT=30] -->
@@ -2837,6 +2840,8 @@
<string name="empty_user_name" translatable="false">Your friend</string>
<!-- Empty status shown before user has selected a friend [CHAR LIMIT=30] -->
<string name="empty_status" translatable="false">Their status</string>
+ <!-- Default text for missed call notifications [CHAR LIMIT=30] -->
+ <string name="missed_call" translatable="false">Missed call</string>
<!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 0a117c1..1569fff 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -21,9 +21,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
-import android.graphics.Paint;
import android.icu.text.NumberFormat;
-import android.util.MathUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -94,11 +92,6 @@
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
- float getClockTextTopPadding() {
- Paint.FontMetrics fm = mView.getPaint().getFontMetrics();
- return MathUtils.abs(fm.ascent - fm.top);
- }
-
/**
* Updates the time for the view.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
index f01b67b..945c9c4 100644
--- a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
+++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
@@ -20,6 +20,7 @@
import android.hardware.biometrics.BiometricSourceType;
import android.view.View;
+import android.view.ViewGroup;
import androidx.annotation.NonNull;
@@ -73,13 +74,15 @@
@Override
protected void onViewAttached() {
- mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
-
- mStatusBarStateController.addCallback(mStatusBarStateListener);
mIsKeyguardShowing = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
mIsDozing = mStatusBarStateController.isDozing();
+ mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
mAuthenticated = false;
+ updateButtonVisibility();
+
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
}
@Override
@@ -88,6 +91,15 @@
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
+ /**
+ * Call when this controller is no longer needed. This will remove the view from its parent.
+ */
+ public void destroy() {
+ if (mView != null && mView.getParent() != null) {
+ ((ViewGroup) mView.getParent()).removeView(mView);
+ }
+ }
+
private void updateButtonVisibility() {
mShowButton = !mAuthenticated && !mIsDozing && mIsKeyguardShowing
&& !mIsBouncerShowing && !mRunningFPS;
@@ -143,6 +155,14 @@
public void onBiometricRunningStateChanged(boolean running,
BiometricSourceType biometricSourceType) {
mRunningFPS = running && biometricSourceType == FINGERPRINT;
+ mAuthenticated &= !mRunningFPS;
+ updateButtonVisibility();
+ }
+
+ @Override
+ public void onBiometricAuthenticated(int userId,
+ BiometricSourceType biometricSourceType, boolean isStrongBiometric) {
+ mAuthenticated = true;
updateButtonVisibility();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 6eb54c2..93ed0ea 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -179,20 +179,15 @@
setPaddingRelative(startEndPadding, 0, startEndPadding, 0);
mSmallClockFrame.setVisibility(GONE);
mNewLockscreenClockFrame.setVisibility(VISIBLE);
-
- statusAreaLP.removeRule(RelativeLayout.BELOW);
+ statusAreaLP.addRule(RelativeLayout.BELOW, R.id.new_lockscreen_clock_view);
statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START);
- statusAreaLP.addRule(RelativeLayout.START_OF, R.id.new_lockscreen_clock_view);
- statusAreaLP.width = 0;
} else {
setPaddingRelative(0, 0, 0, 0);
mSmallClockFrame.setVisibility(VISIBLE);
mNewLockscreenClockFrame.setVisibility(GONE);
statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START);
- statusAreaLP.removeRule(RelativeLayout.START_OF);
statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view);
- statusAreaLP.width = ViewGroup.LayoutParams.MATCH_PARENT;
}
requestLayout();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index e375877..0675200 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -20,6 +20,7 @@
import android.content.ContentResolver;
import android.content.res.Resources;
import android.provider.Settings;
+import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.View;
import android.view.ViewGroup;
@@ -212,10 +213,10 @@
* keep the clock centered.
*/
void updatePosition(int x, float scale, AnimationProperties props, boolean animate) {
- x = Math.abs(x);
+ x = getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -x : x;
if (mNewLockScreenClockFrame != null) {
PropertyAnimator.setProperty(mNewLockScreenClockFrame, AnimatableProperty.TRANSLATION_X,
- -x, props, animate);
+ x, props, animate);
PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_X,
scale, props, animate);
PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_Y,
@@ -277,15 +278,6 @@
refreshFormat(mTimeFormat);
}
- float getClockTextTopPadding() {
- if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1
- && mNewLockScreenClockViewController != null) {
- return mNewLockScreenClockViewController.getClockTextTopPadding();
- }
-
- return mView.getClockTextTopPadding();
- }
-
private void updateAodIcons() {
NotificationIconContainer nic = (NotificationIconContainer)
mView.findViewById(
@@ -337,4 +329,8 @@
sCacheKey = key;
}
}
+
+ private int getCurrentLayoutDirection() {
+ return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 92b65b2..533bec1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -136,14 +136,23 @@
@Override
public void startAppearAnimation() {
- // Reset state, and let IME animation reveal the view as it slides in
+ // Reset state, and let IME animation reveal the view as it slides in, if one exists.
+ // It is possible for an IME to have no view, so provide a default animation since no
+ // calls to animateForIme would occur
setAlpha(0f);
+ animate()
+ .alpha(1f)
+ .setDuration(500)
+ .setStartDelay(300)
+ .start();
+
setTranslationY(0f);
}
@Override
public void animateForIme(float interpolatedFraction) {
- setAlpha(interpolatedFraction);
+ animate().cancel();
+ setAlpha(Math.max(interpolatedFraction, getAlpha()));
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index fb97a30..2373d75 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -85,10 +85,6 @@
*/
private Runnable mContentChangeListener;
private boolean mHasHeader;
- private final int mRowWithHeaderPadding;
- private final int mRowPadding;
- private float mRowTextSize;
- private float mRowWithHeaderTextSize;
private View.OnClickListener mOnClickListener;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
@@ -97,9 +93,6 @@
super(context, attrs);
Resources resources = context.getResources();
- mRowPadding = resources.getDimensionPixelSize(R.dimen.subtitle_clock_padding);
- mRowWithHeaderPadding = resources.getDimensionPixelSize(R.dimen.header_subtitle_padding);
-
mLayoutTransition = new LayoutTransition();
mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
mLayoutTransition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION);
@@ -120,10 +113,6 @@
mTextColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size);
mIconSizeWithHeader = (int) mContext.getResources().getDimension(R.dimen.header_icon_size);
- mRowTextSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.widget_label_font_size);
- mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.header_row_font_size);
mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED);
}
@@ -204,7 +193,6 @@
LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams();
layoutParams.gravity = mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL
? Gravity.START : Gravity.CENTER;
- layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding;
mRow.setLayoutParams(layoutParams);
for (int i = startIndex; i < subItemsCount; i++) {
@@ -230,8 +218,6 @@
final SliceItem titleItem = rc.getTitleItem();
button.setText(titleItem == null ? null : titleItem.getText());
button.setContentDescription(rc.getContentDescription());
- button.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- mHasHeader ? mRowWithHeaderTextSize : mRowTextSize);
Drawable iconDrawable = null;
SliceItem icon = SliceQuery.find(item.getSlice(),
@@ -313,10 +299,6 @@
void onDensityOrFontScaleChanged() {
mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size);
mIconSizeWithHeader = (int) mContext.getResources().getDimension(R.dimen.header_icon_size);
- mRowTextSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.widget_label_font_size);
- mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.header_row_font_size);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 6fb6760..934e768 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -266,15 +266,9 @@
mKeyguardClockSwitchController.updateLockScreenMode(mode);
mKeyguardSliceViewController.updateLockScreenMode(mode);
if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
- // align the top of keyguard_status_area with the top of the clock text instead
- // of the top of the view
- mKeyguardSliceViewController.updateTopMargin(
- mKeyguardClockSwitchController.getClockTextTopPadding());
mView.setCanShowOwnerInfo(false);
mView.setCanShowLogout(false);
} else {
- // reset margin
- mKeyguardSliceViewController.updateTopMargin(0);
mView.setCanShowOwnerInfo(true);
mView.setCanShowLogout(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index d8ca639..9686c91f 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -473,7 +473,8 @@
@Override
public void onOpNoted(int code, int uid, String packageName,
- @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
+ String attributionTag, @AppOpsManager.OpFlags int flags,
+ @AppOpsManager.Mode int result) {
if (DEBUG) {
Log.w(TAG, "Noted op: " + code + " with result "
+ AppOpsManager.MODE_NAMES[result] + " for package " + packageName);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
index 3ea8140..3bf75d1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
@@ -19,8 +19,10 @@
import android.content.Context;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.systemui.R;
@@ -33,6 +35,7 @@
@NonNull protected final Context mContext;
@NonNull protected final Drawable mFingerprintDrawable;
+ @Nullable private View mView;
public UdfpsAnimation(@NonNull Context context) {
mContext = context;
@@ -53,6 +56,10 @@
mFingerprintDrawable.setAlpha(alpha);
}
+ public void setAnimationView(UdfpsAnimationView view) {
+ mView = view;
+ }
+
/**
* @return The amount of padding that's needed on each side of the sensor, in pixels.
*/
@@ -66,4 +73,10 @@
public int getPaddingY() {
return 0;
}
+
+ protected void postInvalidateView() {
+ if (mView != null) {
+ mView.postInvalidate();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
index 501de9d..8664e44 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
@@ -23,7 +23,6 @@
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.util.MathUtils;
-import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -42,7 +41,6 @@
private static final String TAG = "UdfpsAnimationKeyguard";
- @NonNull private final View mParent;
@NonNull private final Context mContext;
private final int mMaxBurnInOffsetX;
private final int mMaxBurnInOffsetY;
@@ -52,10 +50,9 @@
private float mBurnInOffsetX;
private float mBurnInOffsetY;
- UdfpsAnimationKeyguard(@NonNull View parent, @NonNull Context context,
+ UdfpsAnimationKeyguard(@NonNull Context context,
@NonNull StatusBarStateController statusBarStateController) {
super(context);
- mParent = parent;
mContext = context;
mMaxBurnInOffsetX = context.getResources()
@@ -73,10 +70,10 @@
mInterpolatedDarkAmount);
mBurnInOffsetY = MathUtils.lerp(0f,
getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
- - 0.5f * mMaxBurnInOffsetY,
+ - mMaxBurnInOffsetY,
mInterpolatedDarkAmount);
updateColor();
- mParent.postInvalidate();
+ postInvalidateView();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 41ea4d6..44122cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -74,7 +74,14 @@
}
void setAnimation(@Nullable UdfpsAnimation animation) {
+ if (mUdfpsAnimation != null) {
+ mUdfpsAnimation.setAnimationView(null);
+ }
+
mUdfpsAnimation = animation;
+ if (mUdfpsAnimation != null) {
+ mUdfpsAnimation.setAnimationView(this);
+ }
}
void onSensorRectUpdated(@NonNull RectF sensorRect) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index e7b08e7..6451ad9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -44,7 +44,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -324,7 +323,7 @@
case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
return new UdfpsAnimationEnroll(mContext);
case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
- return new UdfpsAnimationKeyguard(mView, mContext, mStatusBarStateController);
+ return new UdfpsAnimationKeyguard(mContext, mStatusBarStateController);
case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
return new UdfpsAnimationFpmOther(mContext);
default:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 00cb28b..6ffecdb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -113,6 +113,7 @@
void setExtras(@Nullable UdfpsAnimation animation, @Nullable UdfpsEnrollHelper enrollHelper) {
mAnimationView.setAnimation(animation);
+
mEnrollHelper = enrollHelper;
if (enrollHelper != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
index 36937d6..6b7a1ac 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
@@ -91,7 +91,7 @@
}
/**
- * @return true if controls are feature-enabled and have available services to serve controls
+ * @return true if controls are feature-enabled and the user has the setting enabled
*/
fun isEnabled() = featureEnabled && lazyControlsController.get().available
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 2b362b9..8d2639d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -22,6 +22,7 @@
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.screenrecord.ScreenRecordDialog;
+import com.android.systemui.screenshot.LongScreenshotActivity;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
import com.android.systemui.tuner.TunerActivity;
@@ -99,4 +100,10 @@
@IntoMap
@ClassKey(PeopleSpaceActivity.class)
public abstract Activity bindPeopleSpaceActivity(PeopleSpaceActivity activity);
+
+ /** Inject into LongScreenshotActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(LongScreenshotActivity.class)
+ public abstract Activity bindLongScreenshotActivity(LongScreenshotActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index ec3188a..5d226d5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -29,6 +29,7 @@
import com.android.systemui.media.systemsounds.HomeSoundEffectController;
import com.android.systemui.people.widget.PeopleSpaceWidgetEnabler;
import com.android.systemui.power.PowerUI;
+import com.android.systemui.privacy.television.TvOngoingPrivacyChip;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsModule;
import com.android.systemui.shortcut.ShortcutKeyDispatcher;
@@ -155,6 +156,12 @@
@ClassKey(TvNotificationPanel.class)
public abstract SystemUI bindsTvNotificationPanel(TvNotificationPanel sysui);
+ /** Inject into TvOngoingPrivacyChip. */
+ @Binds
+ @IntoMap
+ @ClassKey(TvOngoingPrivacyChip.class)
+ public abstract SystemUI bindsTvOngoingPrivacyChip(TvOngoingPrivacyChip sysui);
+
/** Inject into VolumeUI. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 8ab135c..4418696 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -32,6 +32,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.AlarmTimeout;
@@ -41,12 +42,15 @@
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
* The policy controlling doze.
*/
@DozeScope
public class DozeUi implements DozeMachine.Part, TunerService.Tunable {
-
+ // if enabled, calls dozeTimeTick() whenever the time changes:
+ private static final boolean BURN_IN_TESTING_ENABLED = false;
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
private final Context mContext;
private final DozeHost mHost;
@@ -57,16 +61,28 @@
private final boolean mCanAnimateTransition;
private final DozeParameters mDozeParameters;
private final DozeLog mDozeLog;
+ private final Lazy<StatusBarStateController> mStatusBarStateController;
private boolean mKeyguardShowing;
private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
new KeyguardUpdateMonitorCallback() {
-
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
mKeyguardShowing = showing;
updateAnimateScreenOff();
}
+
+ @Override
+ public void onTimeChanged() {
+ if (BURN_IN_TESTING_ENABLED && mStatusBarStateController != null
+ && mStatusBarStateController.get().isDozing()) {
+ // update whenever the time changes for manual burn in testing
+ mHost.dozeTimeTick();
+
+ // Keep wakelock until a frame has been pushed.
+ mHandler.post(mWakeLock.wrap(() -> {}));
+ }
+ }
};
private long mLastTimeTickElapsed = 0;
@@ -75,7 +91,8 @@
public DozeUi(Context context, AlarmManager alarmManager,
WakeLock wakeLock, DozeHost host, @Main Handler handler,
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
- DozeLog dozeLog, TunerService tunerService) {
+ DozeLog dozeLog, TunerService tunerService,
+ Lazy<StatusBarStateController> statusBarStateController) {
mContext = context;
mWakeLock = wakeLock;
mHost = host;
@@ -85,8 +102,8 @@
mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
mDozeLog = dozeLog;
-
tunerService.addTunable(this, Settings.Secure.DOZE_ALWAYS_ON);
+ mStatusBarStateController = statusBarStateController;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 27ea64f8..70b7b04 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -25,7 +25,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -54,7 +53,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -116,7 +114,7 @@
// Tracks config changes that will actually recreate the nav bar
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
- | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS
+ | ActivityInfo.CONFIG_SCREEN_LAYOUT
| ActivityInfo.CONFIG_UI_MODE);
@Inject
@@ -171,6 +169,7 @@
configurationController.addCallback(this);
mConfigChanges.applyNewConfig(mContext.getResources());
mNavBarOverlayController = navBarOverlayController;
+ mNavigationModeController.addListener(this);
}
@Override
@@ -188,17 +187,18 @@
@Override
public void onNavigationModeChanged(int mode) {
- // Workaround for b/132825155, for secondary users, we currently don't receive configuration
- // changes on overlay package change since SystemUI runs for the system user. In this case,
- // trigger a new configuration change to ensure that the nav bar is updated in the same way.
- int userId = ActivityManagerWrapper.getInstance().getCurrentUserId();
- if (userId != UserHandle.USER_SYSTEM) {
- mHandler.post(() -> {
- for (int i = 0; i < mNavigationBars.size(); i++) {
- recreateNavigationBar(mNavigationBars.keyAt(i));
+ mHandler.post(() -> {
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ NavigationBar navBar = mNavigationBars.valueAt(i);
+ if (navBar == null) {
+ continue;
}
- });
- }
+ NavigationBarView view = (NavigationBarView) mNavigationBars.get(i).getView();
+ if (view != null) {
+ view.updateStates();
+ }
+ }
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
index c526c5d..62b9458 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
@@ -21,6 +21,7 @@
import android.view.View;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.FeatureFlags;
import java.util.function.Consumer;
@@ -31,16 +32,22 @@
public class NavigationBarOverlayController {
protected final Context mContext;
+ protected final FeatureFlags mFeatureFlags;
@Inject
- public NavigationBarOverlayController(Context context) {
+ public NavigationBarOverlayController(Context context, FeatureFlags featureFlags) {
mContext = context;
+ mFeatureFlags = featureFlags;
}
public Context getContext() {
return mContext;
}
+ public boolean isNavigationBarOverlayEnabled() {
+ return mFeatureFlags.isNavigationBarOverlayEnabled();
+ }
+
/**
* Initialize the controller with visibility change callback and light/dark icon color.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index b55fa4d..61e1d61 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -192,6 +192,7 @@
buttonDispatchers.valueAt(i).setDarkIntensity(darkIntensity);
}
mView.getRotationButtonController().setDarkIntensity(darkIntensity);
+
Dependency.get(NavigationBarOverlayController.class).setDarkIntensity(darkIntensity);
for (DarkIntensityListener listener : mDarkIntensityListeners) {
listener.onDarkIntensity(darkIntensity);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 8e75bec..19e3278 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -171,6 +171,7 @@
private NotificationPanelViewController mPanelView;
private FloatingRotationButton mFloatingRotationButton;
private RotationButtonController mRotationButtonController;
+ private NavigationBarOverlayController mNavBarOverlayController;
/**
* Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -339,8 +340,11 @@
isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton,
mRotationButtonListener);
- Dependency.get(NavigationBarOverlayController.class).init(
- mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor);
+ mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.init(
+ mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor);
+ }
mConfiguration = new Configuration();
mTmpLastConfiguration = new Configuration();
@@ -431,8 +435,9 @@
// The visibility of the navigation bar buttons is dependent on the transient state of
// the navigation bar.
- Dependency.get(NavigationBarOverlayController.class).setButtonState(
- isTransient, /* force */ false);
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.setButtonState(isTransient, /* force */ false);
+ }
}
void onBarTransition(int newMode) {
@@ -666,7 +671,9 @@
}
mImeVisible = visible;
mRotationButtonController.getRotationButton().setCanShowRotationButton(!mImeVisible);
- Dependency.get(NavigationBarOverlayController.class).setCanShow(!mImeVisible);
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.setCanShow(!mImeVisible);
+ }
}
public void setDisabledFlags(int disabledFlags) {
@@ -999,10 +1006,9 @@
} else {
updateButtonLocation(getRotateSuggestionButton(), inScreenSpace);
}
- final NavigationBarOverlayController navBarButtonsController =
- Dependency.get(NavigationBarOverlayController.class);
- if (navBarButtonsController.isVisible()) {
- updateButtonLocation(navBarButtonsController.getCurrentView(), inScreenSpace);
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()
+ && mNavBarOverlayController.isVisible()) {
+ updateButtonLocation(mNavBarOverlayController.getCurrentView(), inScreenSpace);
}
return mTmpRegion;
}
@@ -1230,7 +1236,9 @@
if (mRotationButtonController != null) {
mRotationButtonController.registerListeners();
}
- Dependency.get(NavigationBarOverlayController.class).registerListeners();
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.registerListeners();
+ }
getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
updateNavButtonIcons();
@@ -1247,7 +1255,10 @@
if (mRotationButtonController != null) {
mRotationButtonController.unregisterListeners();
}
- Dependency.get(NavigationBarOverlayController.class).unregisterListeners();
+
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.unregisterListeners();
+ }
mEdgeBackGestureHandler.onNavBarDetached();
getViewTreeObserver().removeOnComputeInternalInsetsListener(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 292cc7a..088743c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -93,6 +93,9 @@
private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
"gestures.back_timeout", 250);
+ private static final int MAX_NUM_LOGGED_PREDICTIONS = 10;
+ private static final int MAX_NUM_LOGGED_GESTURES = 10;
+
// Temporary log until b/176302696 is resolved
static final boolean DEBUG_MISSING_GESTURE = true;
static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture";
@@ -222,8 +225,9 @@
private String mPackageName;
private float mMLResults;
- private static final int MAX_LOGGED_PREDICTIONS = 10;
+ // For debugging
private ArrayDeque<String> mPredictionLog = new ArrayDeque<>();
+ private ArrayDeque<String> mGestureLog = new ArrayDeque<>();
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
@@ -607,7 +611,7 @@
}
// Check if we are within the tightest bounds beyond which
// we would not need to run the ML model.
- boolean withinRange = x <= mMLEnableWidth + mLeftInset
+ boolean withinRange = x < mMLEnableWidth + mLeftInset
|| x >= (mDisplaySize.x - mMLEnableWidth - mRightInset);
if (!withinRange) {
int results = -1;
@@ -617,17 +621,20 @@
// Denotes whether we should proceed with the gesture.
// Even if it is false, we may want to log it assuming
// it is not invalid due to exclusion.
- withinRange = x <= mEdgeWidthLeft + mLeftInset
+ withinRange = x < mEdgeWidthLeft + mLeftInset
|| x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
}
}
// For debugging purposes
- if (mPredictionLog.size() >= MAX_LOGGED_PREDICTIONS) {
+ if (mPredictionLog.size() >= MAX_NUM_LOGGED_PREDICTIONS) {
mPredictionLog.removeFirst();
}
- mPredictionLog.addLast(String.format("[%d,%d,%d,%f,%d]",
- x, y, app, mMLResults, withinRange ? 1 : 0));
+ mPredictionLog.addLast(String.format("Prediction [%d,%d,%d,%d,%f,%d]",
+ System.currentTimeMillis(), x, y, app, mMLResults, withinRange ? 1 : 0));
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, mPredictionLog.peekLast());
+ }
// Always allow if the user is in a transient sticky immersive state
if (mIsNavBarShownTransiently) {
@@ -689,6 +696,10 @@
private void onMotionEvent(MotionEvent ev) {
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev);
+ }
+
// Verify if this is in within the touch region and we aren't in immersive mode, and
// either the bouncer is showing or the notification panel is hidden
mInputEventReceiver.setBatchingEnabled(false);
@@ -709,6 +720,19 @@
mEndPoint.set(-1, -1);
mThresholdCrossed = false;
}
+
+ // For debugging purposes
+ if (mGestureLog.size() >= MAX_NUM_LOGGED_GESTURES) {
+ mGestureLog.removeFirst();
+ }
+ mGestureLog.addLast(String.format(
+ "Gesture [%d,alw=%B,%B, %B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]",
+ System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, mIsBackGestureAllowed,
+ QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize,
+ mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion));
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, mGestureLog.peekLast());
+ }
} else if (mAllowGesture || mLogGesture) {
if (!mThresholdCrossed) {
mEndPoint.x = (int) ev.getX();
@@ -827,18 +851,29 @@
public void dump(PrintWriter pw) {
pw.println("EdgeBackGestureHandler:");
pw.println(" mIsEnabled=" + mIsEnabled);
+ pw.println(" mIsAttached=" + mIsAttached);
pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed);
+ pw.println(" mIsGesturalModeEnabled=" + mIsGesturalModeEnabled);
+ pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently);
+ pw.println(" mGestureBlockingActivityRunning=" + mGestureBlockingActivityRunning);
pw.println(" mAllowGesture=" + mAllowGesture);
+ pw.println(" mUseMLModel=" + mUseMLModel);
pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep);
pw.println(" mStartingQuickstepRotation=" + mStartingQuickstepRotation);
pw.println(" mInRejectedExclusion" + mInRejectedExclusion);
pw.println(" mExcludeRegion=" + mExcludeRegion);
pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
- pw.println(" mIsAttached=" + mIsAttached);
+ pw.println(" mPipExcludedBounds=" + mPipExcludedBounds);
pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft);
pw.println(" mEdgeWidthRight=" + mEdgeWidthRight);
- pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently);
- pw.println(" mPredictionLog=" + String.join(";", mPredictionLog));
+ pw.println(" mLeftInset=" + mLeftInset);
+ pw.println(" mRightInset=" + mRightInset);
+ pw.println(" mMLEnableWidth=" + mMLEnableWidth);
+ pw.println(" mMLModelThreshold=" + mMLModelThreshold);
+ pw.println(" mTouchSlop=" + mTouchSlop);
+ pw.println(" mBottomGestureHeight=" + mBottomGestureHeight);
+ pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog));
+ pw.println(" mGestureLog=" + String.join("\n", mGestureLog));
pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin);
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index cd1131b..5dda23e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -16,6 +16,7 @@
package com.android.systemui.people;
+import static android.app.Notification.CATEGORY_MISSED_CALL;
import static android.app.Notification.EXTRA_MESSAGES;
import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
@@ -189,7 +190,7 @@
tiles.addAll(recentTiles);
}
- tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager);
+ tiles = augmentTilesFromVisibleNotifications(context, tiles, notificationEntryManager);
return tiles;
}
@@ -357,8 +358,8 @@
&& storedUserId == userId;
}
- static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(List<PeopleSpaceTile> tiles,
- NotificationEntryManager notificationEntryManager) {
+ static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context,
+ List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) {
if (notificationEntryManager == null) {
Log.w(TAG, "NotificationEntryManager is null");
return tiles;
@@ -374,12 +375,13 @@
}
return tiles
.stream()
- .map(entry -> augmentTileFromVisibleNotifications(entry, visibleNotifications))
+ .map(entry -> augmentTileFromVisibleNotifications(
+ context, entry, visibleNotifications))
.collect(Collectors.toList());
}
- static PeopleSpaceTile augmentTileFromVisibleNotifications(PeopleSpaceTile tile,
- Map<String, NotificationEntry> visibleNotifications) {
+ static PeopleSpaceTile augmentTileFromVisibleNotifications(Context context,
+ PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) {
String shortcutId = tile.getId();
String packageName = tile.getPackageName();
int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
@@ -389,7 +391,7 @@
return tile;
}
if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key);
- return augmentTileFromNotification(tile, visibleNotifications.get(key).getSbn());
+ return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn());
}
/**
@@ -408,7 +410,7 @@
}
if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
- storedTile = augmentTileFromNotification(storedTile, sbn);
+ storedTile = augmentTileFromNotification(context, storedTile, sbn);
} else {
if (DEBUG) {
Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
@@ -418,23 +420,40 @@
.setNotificationKey(null)
.setNotificationContent(null)
.setNotificationDataUri(null)
+ .setNotificationCategory(null)
.build();
}
updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
}
- static PeopleSpaceTile augmentTileFromNotification(PeopleSpaceTile tile,
+ static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile,
StatusBarNotification sbn) {
- Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
- if (message == null) {
- if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
+ Notification notification = sbn.getNotification();
+ if (notification == null) {
+ if (DEBUG) Log.d(TAG, "Notification is null");
return tile;
}
+ boolean isMissedCall = Objects.equals(notification.category, CATEGORY_MISSED_CALL);
+ Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(notification);
+
+ if (!isMissedCall && message == null) {
+ if (DEBUG) Log.d(TAG, "Notification has no content");
+ return tile;
+ }
+
+ // If it's a missed call notification and it doesn't include content, use fallback value,
+ // otherwise, use notification content.
+ boolean hasMessageText = message != null && !TextUtils.isEmpty(message.getText());
+ CharSequence content = (isMissedCall && !hasMessageText)
+ ? context.getString(R.string.missed_call) : message.getText();
+ Uri dataUri = message != null ? message.getDataUri() : null;
+
return tile
.toBuilder()
.setNotificationKey(sbn.getKey())
- .setNotificationContent(message.getText())
- .setNotificationDataUri(message.getDataUri())
+ .setNotificationCategory(notification.category)
+ .setNotificationContent(content)
+ .setNotificationDataUri(dataUri)
.build();
}
@@ -462,6 +481,11 @@
* content, then birthdays, then the most recent status, and finally last interaction.
*/
private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile) {
+ if (Objects.equals(tile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
+ if (DEBUG) Log.d(TAG, "Create missed call view");
+ return createMissedCallRemoteViews(context, tile);
+ }
+
if (tile.getNotificationKey() != null) {
if (DEBUG) Log.d(TAG, "Create notification view");
return createNotificationRemoteViews(context, tile);
@@ -630,6 +654,16 @@
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);
+ views.setBoolean(R.id.content_background, "setClipToOutline", true);
+ return views;
+ }
+
private static RemoteViews createNotificationRemoteViews(Context context,
PeopleSpaceTile tile) {
RemoteViews views = new RemoteViews(
@@ -715,8 +749,7 @@
/** Gets the most recent {@link Notification.MessagingStyle.Message} from the notification. */
@VisibleForTesting
public static Notification.MessagingStyle.Message getLastMessagingStyleMessage(
- StatusBarNotification sbn) {
- Notification notification = sbn.getNotification();
+ Notification notification) {
if (notification == null) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java
index b188acb..3df2644 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java
@@ -23,6 +23,7 @@
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.statusbar.FeatureFlags;
import javax.inject.Inject;
@@ -54,6 +55,12 @@
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
+ mContext.getPackageManager().setComponentEnabledSetting(
+ new ComponentName(mContext, PeopleSpaceActivity.class),
+ showPeopleSpace
+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
} catch (Exception e) {
Log.w(TAG, "Error enabling People Space widget:", e);
}
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 bee9889..9e5c786 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -29,7 +29,6 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
-import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.appwidget.IAppWidgetService;
@@ -124,8 +123,6 @@
*/
public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction) {
- RemoteViews views = new RemoteViews(
- mContext.getPackageName(), R.layout.people_space_small_avatar_tile);
if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
new file mode 100644
index 0000000..0fa7b59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -0,0 +1,334 @@
+/*
+ * 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.privacy.television;
+
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.IntDef;
+import android.annotation.UiThread;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.privacy.PrivacyChipBuilder;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * A SystemUI component responsible for notifying the user whenever an application is
+ * recording audio, accessing the camera or accessing the location.
+ */
+@SysUISingleton
+public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback {
+ private static final String TAG = "TvOngoingPrivacyChip";
+ static final boolean DEBUG = false;
+
+ // This title is used in CameraMicIndicatorsPermissionTest and
+ // RecognitionServiceMicIndicatorTest.
+ private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"STATE_"}, value = {
+ STATE_NOT_SHOWN,
+ STATE_APPEARING,
+ STATE_SHOWN,
+ STATE_DISAPPEARING
+ })
+ public @interface State {
+ }
+
+ private static final int STATE_NOT_SHOWN = 0;
+ private static final int STATE_APPEARING = 1;
+ private static final int STATE_SHOWN = 2;
+ private static final int STATE_DISAPPEARING = 3;
+
+ private static final int ANIMATION_DURATION_MS = 200;
+
+ private final Context mContext;
+ private final PrivacyItemController mPrivacyItemController;
+
+ private View mIndicatorView;
+ private boolean mViewAndWindowAdded;
+ private ObjectAnimator mAnimator;
+
+ private boolean mAllIndicatorsFlagEnabled;
+ private boolean mMicCameraIndicatorFlagEnabled;
+ private boolean mLocationIndicatorEnabled;
+ private List<PrivacyItem> mPrivacyItems;
+
+ private LinearLayout mIconsContainer;
+ private final int mIconSize;
+ private final int mIconMarginStart;
+
+ @State
+ private int mState = STATE_NOT_SHOWN;
+
+ @Inject
+ public TvOngoingPrivacyChip(Context context, PrivacyItemController privacyItemController) {
+ super(context);
+ Log.d(TAG, "Privacy chip running without id");
+ mContext = context;
+ mPrivacyItemController = privacyItemController;
+
+ Resources res = mContext.getResources();
+ mIconMarginStart = Math.round(res.getDimension(R.dimen.privacy_chip_icon_margin));
+ mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
+
+ mAllIndicatorsFlagEnabled = privacyItemController.getAllIndicatorsAvailable();
+ mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable();
+ mLocationIndicatorEnabled = privacyItemController.getLocationAvailable();
+
+ if (DEBUG) {
+ Log.d(TAG, "allIndicators: " + mAllIndicatorsFlagEnabled);
+ Log.d(TAG, "micCameraIndicators: " + mMicCameraIndicatorFlagEnabled);
+ Log.d(TAG, "locationIndicators: " + mLocationIndicatorEnabled);
+ }
+ }
+
+ @Override
+ public void start() {
+ mPrivacyItemController.addCallback(this);
+ }
+
+ @Override
+ public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+ if (DEBUG) Log.d(TAG, "PrivacyItemsChanged");
+ mPrivacyItems = privacyItems;
+ updateUI();
+ }
+
+ @Override
+ public void onFlagAllChanged(boolean flag) {
+ if (DEBUG) Log.d(TAG, "all indicators enabled: " + flag);
+ mAllIndicatorsFlagEnabled = flag;
+ }
+
+ @Override
+ public void onFlagMicCameraChanged(boolean flag) {
+ if (DEBUG) Log.d(TAG, "mic/camera indicators enabled: " + flag);
+ mMicCameraIndicatorFlagEnabled = flag;
+ }
+
+ @Override
+ public void onFlagLocationChanged(boolean flag) {
+ if (DEBUG) Log.d(TAG, "location indicators enabled: " + flag);
+ mLocationIndicatorEnabled = flag;
+ }
+
+ private void updateUI() {
+ if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items");
+
+ if ((mMicCameraIndicatorFlagEnabled || mAllIndicatorsFlagEnabled
+ || mLocationIndicatorEnabled) && !mPrivacyItems.isEmpty()) {
+ if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) {
+ showIndicator();
+ } else {
+ if (DEBUG) Log.d(TAG, "only updating icons");
+ PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems);
+ setIcons(builder.generateIcons(), mIconsContainer);
+ mIconsContainer.requestLayout();
+ }
+ } else {
+ hideIndicatorIfNeeded();
+ }
+ }
+
+ @UiThread
+ private void hideIndicatorIfNeeded() {
+ if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) return;
+
+ if (mViewAndWindowAdded) {
+ mState = STATE_DISAPPEARING;
+ animateDisappearance();
+ } else {
+ // Appearing animation has not started yet, as we were still waiting for the View to be
+ // laid out.
+ mState = STATE_NOT_SHOWN;
+ removeIndicatorView();
+ }
+ }
+
+ @UiThread
+ private void showIndicator() {
+ mState = STATE_APPEARING;
+
+ // Inflate the indicator view
+ mIndicatorView = LayoutInflater.from(mContext).inflate(
+ R.layout.tv_ongoing_privacy_chip, null);
+
+ // 1. Set alpha to 0.
+ // 2. Wait until the window is shown and the view is laid out.
+ // 3. Start a "fade in" (alpha) animation.
+ mIndicatorView.setAlpha(0f);
+ mIndicatorView
+ .getViewTreeObserver()
+ .addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ // State could have changed to NOT_SHOWN (if all the recorders are
+ // already gone)
+ if (mState != STATE_APPEARING) return;
+
+ mViewAndWindowAdded = true;
+ // Remove the observer
+ mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
+ this);
+
+ animateAppearance();
+ }
+ });
+
+ mIconsContainer = mIndicatorView.findViewById(R.id.icons_container);
+ PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems);
+ setIcons(builder.generateIcons(), mIconsContainer);
+
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
+ WRAP_CONTENT,
+ WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.gravity = Gravity.TOP | Gravity.END;
+ layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
+ layoutParams.packageName = mContext.getPackageName();
+ final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ windowManager.addView(mIndicatorView, layoutParams);
+
+ }
+
+ private void setIcons(List<Drawable> icons, ViewGroup iconsContainer) {
+ iconsContainer.removeAllViews();
+ for (int i = 0; i < icons.size(); i++) {
+ Drawable icon = icons.get(i);
+ icon.mutate().setTint(Color.WHITE);
+ ImageView imageView = new ImageView(mContext);
+ imageView.setImageDrawable(icon);
+ imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ mIconsContainer.addView(imageView, mIconSize, mIconSize);
+ if (i != 0) {
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) imageView.getLayoutParams();
+ layoutParams.setMarginStart(mIconMarginStart);
+ imageView.setLayoutParams(layoutParams);
+ }
+ }
+ }
+
+ private void animateAppearance() {
+ animateAlphaTo(1f);
+ }
+
+ private void animateDisappearance() {
+ animateAlphaTo(0f);
+ }
+
+ private void animateAlphaTo(final float endValue) {
+ if (mAnimator == null) {
+ if (DEBUG) Log.d(TAG, "set up animator");
+
+ mAnimator = new ObjectAnimator();
+ mAnimator.setTarget(mIndicatorView);
+ mAnimator.setProperty(View.ALPHA);
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ boolean mCancelled;
+
+ @Override
+ public void onAnimationStart(Animator animation, boolean isReverse) {
+ if (DEBUG) Log.d(TAG, "AnimatorListenerAdapter#onAnimationStart");
+ mCancelled = false;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (DEBUG) Log.d(TAG, "AnimatorListenerAdapter#onAnimationCancel");
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (DEBUG) Log.d(TAG, "AnimatorListenerAdapter#onAnimationEnd");
+ // When ValueAnimator#cancel() is called it always calls onAnimationCancel(...)
+ // and then onAnimationEnd(...). We, however, only want to proceed here if the
+ // animation ended "naturally".
+ if (!mCancelled) {
+ onAnimationFinished();
+ }
+ }
+ });
+ } else if (mAnimator.isRunning()) {
+ if (DEBUG) Log.d(TAG, "cancel running animation");
+ mAnimator.cancel();
+ }
+
+ final float currentValue = mIndicatorView.getAlpha();
+ if (DEBUG) Log.d(TAG, "animate alpha to " + endValue + " from " + currentValue);
+
+ mAnimator.setDuration((int) (Math.abs(currentValue - endValue) * ANIMATION_DURATION_MS));
+ mAnimator.setFloatValues(endValue);
+ mAnimator.start();
+ }
+
+ private void onAnimationFinished() {
+ if (DEBUG) Log.d(TAG, "onAnimationFinished");
+
+ if (mState == STATE_APPEARING) {
+ mState = STATE_SHOWN;
+ } else if (mState == STATE_DISAPPEARING) {
+ removeIndicatorView();
+ mState = STATE_NOT_SHOWN;
+ }
+ }
+
+ private void removeIndicatorView() {
+ if (DEBUG) Log.d(TAG, "removeIndicatorView");
+
+ final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ if (windowManager != null) {
+ windowManager.removeView(mIndicatorView);
+ }
+
+ mIndicatorView = null;
+ mAnimator = null;
+
+ mViewAndWindowAdded = false;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 8110fda..16e5196 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -95,7 +95,6 @@
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
if (isTunerEnabled()) {
mTunerService.showResetRequest(
- mUserTracker.getUserHandle(),
() -> {
// Relaunch settings so that the tuner disappears.
startSettingsActivity();
@@ -103,7 +102,7 @@
} else {
Toast.makeText(getContext(), R.string.tuner_toast,
Toast.LENGTH_LONG).show();
- mTunerService.setTunerEnabled(mUserTracker.getUserHandle(), true);
+ mTunerService.setTunerEnabled(true);
}
startSettingsActivity();
@@ -238,6 +237,6 @@
}
private boolean isTunerEnabled() {
- return mTunerService.isTunerEnabled(mUserTracker.getUserHandle());
+ return mTunerService.isTunerEnabled();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 4144591..3b9f5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -25,7 +25,7 @@
import com.android.systemui.R
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
-import com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE
+import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsDialog
import com.android.systemui.dagger.qualifiers.Background
@@ -92,7 +92,7 @@
override fun isAvailable(): Boolean {
return featureFlags.isKeyguardLayoutEnabled &&
controlsLockscreen &&
- controlsComponent.getVisibility() != UNAVAILABLE
+ controlsComponent.getControlsController().isPresent
}
override fun newTileState(): QSTile.State {
@@ -119,7 +119,7 @@
}
override fun handleClick() {
- if (state.state != Tile.STATE_UNAVAILABLE) {
+ if (state.state == Tile.STATE_ACTIVE) {
mUiHandler.post {
createDialog()
controlsDialog?.show(controlsComponent.getControlsUiController().get())
@@ -129,15 +129,21 @@
override fun handleUpdateState(state: QSTile.State, arg: Any?) {
state.label = tileLabel
- state.secondaryLabel = ""
- state.stateDescription = ""
+
state.contentDescription = state.label
state.icon = icon
- if (hasControlsApps.get()) {
- state.state = Tile.STATE_ACTIVE
+ if (controlsComponent.isEnabled() && hasControlsApps.get()) {
if (controlsDialog == null) {
mUiHandler.post(this::createDialog)
}
+ if (controlsComponent.getVisibility() == AVAILABLE) {
+ state.state = Tile.STATE_ACTIVE
+ state.secondaryLabel = ""
+ } else {
+ state.state = Tile.STATE_INACTIVE
+ state.secondaryLabel = mContext.getText(R.string.controls_tile_locked)
+ }
+ state.stateDescription = state.secondaryLabel
} else {
state.state = Tile.STATE_UNAVAILABLE
dismissDialog()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 1386ddf..53d9f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -97,8 +97,8 @@
float bottom = mBottomCrop + mBottomDelta;
drawShade(canvas, 0, top);
drawShade(canvas, bottom, 1f);
- drawHandle(canvas, top);
- drawHandle(canvas, bottom);
+ drawHandle(canvas, top, /* draw the handle tab down */ false);
+ drawHandle(canvas, bottom, /* draw the handle tab up */ true);
}
@Override
@@ -122,7 +122,7 @@
} else { // Bottom
mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta,
topPx + 2 * mCropTouchMargin - bottomPx,
- getMeasuredHeight() - bottomPx));
+ getHeight() - bottomPx));
}
updateListener(event);
invalidate();
@@ -212,21 +212,25 @@
}
private void drawShade(Canvas canvas, float fracStart, float fracEnd) {
- canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(),
+ canvas.drawRect(0, fractionToPixels(fracStart), getWidth(),
fractionToPixels(fracEnd), mShadePaint);
}
- private void drawHandle(Canvas canvas, float frac) {
+ private void drawHandle(Canvas canvas, float frac, boolean handleTabUp) {
int y = fractionToPixels(frac);
- canvas.drawLine(0, y, getMeasuredWidth(), y, mHandlePaint);
+ canvas.drawLine(0, y, getWidth(), y, mHandlePaint);
+ float radius = 15 * getResources().getDisplayMetrics().density;
+ float x = getWidth() * .9f;
+ canvas.drawArc(x - radius, y - radius, x + radius, y + radius, handleTabUp ? 180 : 0, 180,
+ true, mHandlePaint);
}
private int fractionToPixels(float frac) {
- return (int) (frac * getMeasuredHeight());
+ return (int) (frac * getHeight());
}
private float pixelsToFraction(int px) {
- return px / (float) getMeasuredHeight();
+ return px / (float) getHeight();
}
private CropBoundary nearestBoundary(MotionEvent event, int topPx, int bottomPx) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
new file mode 100644
index 0000000..89efda9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -0,0 +1,210 @@
+/*
+ * 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.screenshot;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+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 java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * LongScreenshotActivity acquires bitmap data for a long screenshot and lets the user trim the top
+ * and bottom before saving/sharing/editing.
+ */
+public class LongScreenshotActivity extends Activity {
+ private static final String TAG = "LongScreenshotActivity";
+
+ private final UiEventLogger mUiEventLogger;
+ private final ScrollCaptureController mScrollCaptureController;
+ private final ScrollCaptureClient.Connection mConnection;
+
+ private ImageView mPreview;
+ private View mSave;
+ private View mCancel;
+ private View mEdit;
+ private View mShare;
+ private CropView mCropView;
+ private MagnifierView mMagnifierView;
+
+ private enum PendingAction {
+ SHARE,
+ EDIT,
+ SAVE
+ }
+
+ @Inject
+ public LongScreenshotActivity(UiEventLogger uiEventLogger,
+ ImageExporter imageExporter,
+ @Main Executor mainExecutor,
+ @Background Executor bgExecutor,
+ Context context) {
+ mUiEventLogger = uiEventLogger;
+
+ mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor,
+ imageExporter);
+
+ mConnection = ScreenshotController.takeScrollCaptureConnection();
+ }
+
+ @Override
+ public void onCreate(Bundle 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);
+ mCropView.setCropInteractionListener(mMagnifierView);
+
+ mSave.setOnClickListener(this::onClicked);
+ mCancel.setOnClickListener(this::onClicked);
+ mEdit.setOnClickListener(this::onClicked);
+ mShare.setOnClickListener(this::onClicked);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (mPreview.getDrawable() == null) {
+ if (mConnection == null) {
+ Log.e(TAG, "Failed to get scroll capture connection, bailing out");
+ finishAndRemoveTask();
+ return;
+ }
+ doCapture();
+ }
+ }
+
+ private void setButtonsEnabled(boolean enabled) {
+ mSave.setEnabled(enabled);
+ mCancel.setEnabled(enabled);
+ mEdit.setEnabled(enabled);
+ mShare.setEnabled(enabled);
+ }
+
+ private void doEdit(Uri uri) {
+ String editorPackage = getString(R.string.config_screenshotEditor);
+ Intent intent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ intent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
+ intent.setDataAndType(uri, "image/png");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ startActivityAsUser(intent, UserHandle.CURRENT);
+ finishAndRemoveTask();
+ }
+
+ private void doShare(Uri uri) {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("image/png");
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ Intent sharingChooserIntent = Intent.createChooser(intent, null)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
+ }
+
+ private void onClicked(View v) {
+ int id = v.getId();
+ v.setPressed(true);
+ setButtonsEnabled(false);
+ if (id == R.id.save) {
+ startExport(PendingAction.SAVE);
+ } else if (id == R.id.cancel) {
+ finishAndRemoveTask();
+ } else if (id == R.id.edit) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT);
+ startExport(PendingAction.EDIT);
+ } else if (id == R.id.share) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE);
+ startExport(PendingAction.SHARE);
+ }
+ }
+
+ private void startExport(PendingAction action) {
+ mScrollCaptureController.startExport(mCropView.getTopBoundary(),
+ mCropView.getBottomBoundary(), new ScrollCaptureController.ExportCallback() {
+ @Override
+ public void onError() {
+ Log.e(TAG, "Error exporting image data.");
+ setButtonsEnabled(true);
+ }
+
+ @Override
+ public void onExportComplete(Uri outputUri) {
+ setButtonsEnabled(true);
+ switch (action) {
+ case EDIT:
+ doEdit(outputUri);
+ break;
+ case SHARE:
+ doShare(outputUri);
+ break;
+ case SAVE:
+ // Nothing more to do
+ finishAndRemoveTask();
+ break;
+ }
+ }
+ });
+ }
+
+ private void doCapture() {
+ mScrollCaptureController.start(mConnection,
+ new ScrollCaptureController.ScrollCaptureCallback() {
+ @Override
+ public void onError() {
+ Log.e(TAG, "Error!");
+ finishAndRemoveTask();
+ }
+
+ @Override
+ public void onComplete(ImageTileSet imageTileSet) {
+ Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x "
+ + imageTileSet.getHeight());
+ mPreview.setImageDrawable(imageTileSet.getDrawable());
+ mMagnifierView.setImageTileset(imageTileSet);
+ mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f);
+ }
+ });
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 953b40b..131fde6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -41,6 +41,7 @@
import android.app.WindowContext;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
@@ -100,6 +101,8 @@
public class ScreenshotController {
private static final String TAG = logTag(ScreenshotController.class);
+ private static ScrollCaptureClient.Connection sScrollConnection;
+
/**
* POD used in the AsyncTask which saves an image in the background.
*/
@@ -219,6 +222,12 @@
| 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,
@@ -597,21 +606,12 @@
}
private void runScrollCapture(ScrollCaptureClient.Connection connection) {
- cancelTimeout();
- ScrollCaptureController controller = new ScrollCaptureController(mContext, connection,
- mMainExecutor, mBgExecutor, mImageExporter, mUiEventLogger);
- controller.attach(mWindow);
- controller.start(new TakeScreenshotService.RequestCallback() {
- @Override
- public void reportError() {
- }
+ sScrollConnection = connection; // For LongScreenshotActivity to pick up.
- @Override
- public void onFinish() {
- Log.d(TAG, "onFinish from ScrollCaptureController");
- finishDismiss();
- }
- });
+ Intent intent = new Intent(mContext, LongScreenshotActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mContext.startActivity(intent);
+ dismissScreenshot(false);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index ad5e637..4a3ffa45 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -16,29 +16,16 @@
package com.android.systemui.screenshot;
-import android.annotation.IdRes;
import android.annotation.UiThread;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.UserHandle;
import android.provider.Settings;
-import android.text.TextUtils;
import android.util.Log;
-import android.view.View;
-import android.view.ViewTreeObserver.InternalInsetsInfo;
-import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-import android.view.Window;
-import android.widget.ImageView;
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
-import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
import com.google.common.util.concurrent.ListenableFuture;
@@ -50,7 +37,7 @@
/**
* Interaction controller between the UI and ScrollCaptureClient.
*/
-public class ScrollCaptureController implements OnComputeInternalInsetsListener {
+public class ScrollCaptureController {
private static final String TAG = "ScrollCaptureController";
private static final float MAX_PAGES_DEFAULT = 3f;
@@ -64,188 +51,67 @@
private boolean mAtTopEdge;
private Session mSession;
- // TODO: Support saving without additional action.
- private enum PendingAction {
- SHARE,
- EDIT,
- SAVE
- }
-
public static final int MAX_HEIGHT = 12000;
- private final Connection mConnection;
private final Context mContext;
private final Executor mUiExecutor;
private final Executor mBgExecutor;
private final ImageExporter mImageExporter;
private final ImageTileSet mImageTileSet;
- private final UiEventLogger mUiEventLogger;
private ZonedDateTime mCaptureTime;
private UUID mRequestId;
- private RequestCallback mCallback;
- private Window mWindow;
- private ImageView mPreview;
- private View mSave;
- private View mCancel;
- private View mEdit;
- private View mShare;
- private CropView mCropView;
- private MagnifierView mMagnifierView;
+ private ScrollCaptureCallback mCaptureCallback;
- public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
- Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
+ public ScrollCaptureController(Context context, Executor uiExecutor, Executor bgExecutor,
+ ImageExporter exporter) {
mContext = context;
- mConnection = connection;
mUiExecutor = uiExecutor;
mBgExecutor = bgExecutor;
mImageExporter = exporter;
- mUiEventLogger = uiEventLogger;
mImageTileSet = new ImageTileSet(context.getMainThreadHandler());
}
/**
- * @param window the window to display the preview
- */
- public void attach(Window window) {
- mWindow = window;
- }
-
- /**
* Run scroll capture!
*
+ * @param connection connection to the remote window to be used
* @param callback request callback to report back to the service
*/
- public void start(RequestCallback callback) {
+ public void start(Connection connection, ScrollCaptureCallback callback) {
mCaptureTime = ZonedDateTime.now();
mRequestId = UUID.randomUUID();
- mCallback = callback;
-
- setContentView(R.layout.long_screenshot);
- mWindow.getDecorView().getViewTreeObserver()
- .addOnComputeInternalInsetsListener(this);
- 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);
- mCropView.setCropInteractionListener(mMagnifierView);
-
- mSave.setOnClickListener(this::onClicked);
- mCancel.setOnClickListener(this::onClicked);
- mEdit.setOnClickListener(this::onClicked);
- mShare.setOnClickListener(this::onClicked);
+ mCaptureCallback = callback;
float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(),
SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT);
- mConnection.start(this::startCapture, maxPages);
+ connection.start(this::startCapture, maxPages);
}
-
- /** Ensure the entire window is touchable */
- public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) {
- inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
- }
-
- void disableButtons() {
- mSave.setEnabled(false);
- mCancel.setEnabled(false);
- mEdit.setEnabled(false);
- mShare.setEnabled(false);
- }
-
- private void onClicked(View v) {
- Log.d(TAG, "button clicked!");
-
- int id = v.getId();
- v.setPressed(true);
- disableButtons();
- if (id == R.id.save) {
- startExport(PendingAction.SAVE);
- } else if (id == R.id.cancel) {
- doFinish();
- } else if (id == R.id.edit) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT);
- startExport(PendingAction.EDIT);
- } else if (id == R.id.share) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE);
- startExport(PendingAction.SHARE);
- }
- }
-
- private void doFinish() {
- mPreview.setImageDrawable(null);
- mMagnifierView.setImageTileset(null);
- mImageTileSet.clear();
- mCallback.onFinish();
- mWindow.getDecorView().getViewTreeObserver()
- .removeOnComputeInternalInsetsListener(this);
- }
-
- private void startExport(PendingAction action) {
+ /**
+ * @param topCrop [0,1) fraction of the top of the image to be cropped out.
+ * @param bottomCrop (0, 1] fraction to be cropped out, e.g. 0.7 will crop out the bottom 30%.
+ */
+ public void startExport(float topCrop, float bottomCrop, ExportCallback callback) {
Rect croppedPortion = new Rect(
0,
- (int) (mImageTileSet.getHeight() * mCropView.getTopBoundary()),
+ (int) (mImageTileSet.getHeight() * topCrop),
mImageTileSet.getWidth(),
- (int) (mImageTileSet.getHeight() * mCropView.getBottomBoundary()));
+ (int) (mImageTileSet.getHeight() * bottomCrop));
ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
mBgExecutor, mRequestId, mImageTileSet.toBitmap(croppedPortion), mCaptureTime);
exportFuture.addListener(() -> {
try {
ImageExporter.Result result = exportFuture.get();
- if (action == PendingAction.EDIT) {
- doEdit(result.uri);
- } else if (action == PendingAction.SHARE) {
- doShare(result.uri);
- }
- doFinish();
+ callback.onExportComplete(result.uri);
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "failed to export", e);
- mCallback.onFinish();
+ callback.onError();
}
}, mUiExecutor);
}
- private void doEdit(Uri uri) {
- String editorPackage = mContext.getString(R.string.config_screenshotEditor);
- Intent intent = new Intent(Intent.ACTION_EDIT);
- if (!TextUtils.isEmpty(editorPackage)) {
- intent.setComponent(ComponentName.unflattenFromString(editorPackage));
- }
- intent.setType("image/png");
- intent.setData(uri);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- }
-
- private void doShare(Uri uri) {
- Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType("image/png");
- intent.setData(uri);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_GRANT_READ_URI_PERMISSION);
- Intent sharingChooserIntent = Intent.createChooser(intent, null)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
- }
-
- private void setContentView(@IdRes int id) {
- mWindow.setContentView(id);
- }
-
- <T extends View> T findViewById(@IdRes int res) {
- return mWindow.findViewById(res);
- }
-
-
private void onCaptureResult(CaptureResult result) {
Log.d(TAG, "onCaptureResult: " + result);
boolean emptyResult = result.captured.height() == 0;
@@ -327,11 +193,26 @@
Log.d(TAG, "afterCaptureComplete");
if (mImageTileSet.isEmpty()) {
- session.end(mCallback::onFinish);
+ mCaptureCallback.onError();
} else {
- mPreview.setImageDrawable(mImageTileSet.getDrawable());
- mMagnifierView.setImageTileset(mImageTileSet);
- mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f);
+ mCaptureCallback.onComplete(mImageTileSet);
}
}
+
+ /**
+ * Callback for image capture completion or error.
+ */
+ public interface ScrollCaptureCallback {
+ void onComplete(ImageTileSet imageTileSet);
+ 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/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index a6aec3b..0b40e22 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -17,8 +17,6 @@
package com.android.systemui.settings.brightness;
import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -29,10 +27,8 @@
import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
-import com.android.systemui.util.RoundedCornerProgressDrawable;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -274,9 +270,6 @@
private BrightnessSlider fromTree(ViewGroup root, boolean useMirror) {
BrightnessSliderView v = root.requireViewById(R.id.brightness_slider);
- // TODO(175026098) Workaround. Remove when b/175026098 is fixed
- applyTheme(v);
-
return new BrightnessSlider(root, v, useMirror);
}
@@ -286,32 +279,5 @@
? R.layout.quick_settings_brightness_dialog_thick
: R.layout.quick_settings_brightness_dialog;
}
-
- private LayerDrawable findProgressClippableDrawable(BrightnessSliderView v) {
- SeekBar b = v.requireViewById(R.id.slider);
- if (b.getProgressDrawable() instanceof LayerDrawable) {
- Drawable progress = ((LayerDrawable) b.getProgressDrawable())
- .findDrawableByLayerId(com.android.internal.R.id.progress);
- if (progress instanceof RoundedCornerProgressDrawable) {
- Drawable inner = ((RoundedCornerProgressDrawable) progress).getDrawable();
- if (inner instanceof LayerDrawable) {
- return (LayerDrawable) inner;
- }
- }
- }
- return null;
- }
-
- private void applyTheme(BrightnessSliderView v) {
- LayerDrawable layer = findProgressClippableDrawable(v);
- if (layer != null) {
- layer.findDrawableByLayerId(R.id.slider_foreground).setTintList(
- Utils.getColorAttr(v.getContext(),
- com.android.internal.R.attr.colorControlActivated));
- layer.findDrawableByLayerId(R.id.slider_icon).setTintList(
- Utils.getColorAttr(v.getContext(),
- com.android.internal.R.attr.colorBackground));
- }
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 862c279..cf77e29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -82,4 +82,8 @@
public boolean isMonetEnabled() {
return mFlagReader.isEnabled(R.bool.flag_monet);
}
+
+ public boolean isNavigationBarOverlayEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
index f2adaf0..9ed9659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
@@ -207,7 +207,7 @@
sb.append(g.toJson());
count++;
}
- mLastSaveLen = count;
+ mLastSaveLen += count;
sb.append("]");
return sb.toString();
}
@@ -249,7 +249,9 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
save();
if (mLastSaveLen >= 0) {
- pw.println(String.valueOf(mLastSaveLen) + " gestures written to " + mLogfile);
+ pw.println(String.valueOf(mLastSaveLen)
+ + " gestures since last dump written to " + mLogfile);
+ mLastSaveLen = 0;
} else {
pw.println("error writing gestures");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 20efa32..d2ddd21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -1002,10 +1002,6 @@
return false;
}
- public void onUiModeChanged() {
- updateBackgroundColors();
- }
-
public void setController(NotificationShelfController notificationShelfController) {
mController = notificationShelfController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index 73c7fd1..f21771a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -128,15 +128,6 @@
// this is a foreground-service disclosure for a user that does not need to show one
return true;
}
- if (getFsc().isSystemAlertNotification(sbn)) {
- final String[] apps = sbn.getNotification().extras.getStringArray(
- Notification.EXTRA_FOREGROUND_APPS);
- if (apps != null && apps.length >= 1) {
- if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
- return true;
- }
- }
- }
if (mIsMediaFlagEnabled && isMediaNotification(sbn)) {
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
index 998ae9e..3a87f68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
@@ -95,18 +95,6 @@
sbn.getUser().getIdentifier())) {
return true;
}
-
- // Filters out system alert notifications when unneeded
- if (mForegroundServiceController.isSystemAlertNotification(sbn)) {
- final String[] apps = sbn.getNotification().extras.getStringArray(
- Notification.EXTRA_FOREGROUND_APPS);
- if (apps != null && apps.length >= 1) {
- if (!mForegroundServiceController.isSystemAlertWarningNeeded(
- sbn.getUser().getIdentifier(), apps[0])) {
- return true;
- }
- }
- }
return false;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 724921b..31d052d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -191,7 +191,10 @@
initDimens();
}
- protected void updateBackgroundColors() {
+ /**
+ * Reload background colors from resources and invalidate views.
+ */
+ public void updateBackgroundColors() {
updateColors();
initBackground();
updateBackgroundTint();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 2c7c5cc..417ff5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -21,6 +21,7 @@
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+import static com.android.systemui.util.Utils.shouldUseSplitNotificationShade;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -83,6 +84,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -453,6 +455,7 @@
private NotificationEntry mTopHeadsUpEntry;
private long mNumHeadsUp;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
+ private final FeatureFlags mFeatureFlags;
private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
new ExpandableView.OnHeightChangedListener() {
@@ -492,8 +495,8 @@
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
SysuiStatusBarStateController statusbarStateController,
- AmbientState ambientState
- ) {
+ AmbientState ambientState,
+ FeatureFlags featureFlags) {
super(context, attrs, 0, 0);
Resources res = getResources();
mSectionsManager = notificationSectionsManager;
@@ -530,6 +533,7 @@
mGroupMembershipManager = groupMembershipManager;
mGroupExpansionManager = groupExpansionManager;
mStatusbarStateController = statusbarStateController;
+ mFeatureFlags = featureFlags;
}
void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
@@ -622,7 +626,12 @@
mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
.getDefaultColor();
updateBackgroundDimming();
- mShelf.onUiModeChanged();
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof ActivatableNotificationView) {
+ ((ActivatableNotificationView) child).updateBackgroundColors();
+ }
+ }
}
@ShadeViewRefactor(RefactorComponent.DECORATOR)
@@ -1156,8 +1165,13 @@
if (stackStartPosition <= stackEndPosition) {
stackHeight = stackEndPosition;
} else {
- stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
- stackEndPosition, mQsExpansionFraction);
+ if (shouldUseSplitNotificationShade(mFeatureFlags, getResources())) {
+ // This prevents notifications from being collapsed when QS is expanded.
+ stackHeight = (int) height;
+ } else {
+ stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
+ stackEndPosition, mQsExpansionFraction);
+ }
}
} else {
stackHeight = (int) height;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index a516742..3997028 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -223,6 +223,7 @@
updateShowEmptyShadeView();
mView.updateCornerRadius();
mView.updateBgColor();
+ mView.updateDecorViews();
mView.reinflateViews();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 8cdaa63..f4830fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -134,7 +134,7 @@
mStackScrollerController = stackScrollerController;
mNotificationPanelViewController = notificationPanelViewController;
notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
- notificationPanelViewController.addVerticalTranslationListener(mUpdatePanelTranslation);
+ notificationPanelViewController.setVerticalTranslationListener(mUpdatePanelTranslation);
notificationPanelViewController.setHeadsUpAppearanceController(this);
mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScrollerController.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
@@ -171,7 +171,7 @@
mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
mWakeUpCoordinator.removeListener(this);
mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
- mNotificationPanelViewController.removeVerticalTranslationListener(mUpdatePanelTranslation);
+ mNotificationPanelViewController.setVerticalTranslationListener(null);
mNotificationPanelViewController.setHeadsUpAppearanceController(null);
mStackScrollerController.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScrollerController.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index a6daed5..d638019 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -89,7 +89,8 @@
private int mNotificationStackHeight;
/**
- * Minimum top margin to avoid overlap with status bar.
+ * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
+ * avatar.
*/
private int mMinTopMargin;
@@ -186,15 +187,15 @@
/**
* Sets up algorithm values.
*/
- public void setup(int statusBarMinHeight, int maxShadeBottom, int notificationStackHeight,
- float panelExpansion, int parentHeight, int keyguardStatusHeight,
- int userSwitchHeight, int clockPreferredY, int userSwitchPreferredY,
- boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount,
- boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon,
- float qsExpansion, int cutoutTopInset) {
- mMinTopMargin = statusBarMinHeight + (showLockIcon
- ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon)
- + userSwitchHeight;
+ public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom,
+ int notificationStackHeight, float panelExpansion, int parentHeight,
+ int keyguardStatusHeight, int userSwitchHeight, int clockPreferredY,
+ int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
+ float emptyDragAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
+ boolean showLockIcon, float qsExpansion, int cutoutTopInset) {
+ mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(showLockIcon
+ ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon,
+ userSwitchHeight);
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
mPanelExpansion = panelExpansion;
@@ -344,8 +345,11 @@
}
private float burnInPreventionOffsetX() {
- return getBurnInOffset(mBurnInPreventionOffsetX * 2, true /* xAxis */)
- - mBurnInPreventionOffsetX;
+ if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ return getBurnInOffset(mBurnInPreventionOffsetX * 2, true /* xAxis */)
+ - mBurnInPreventionOffsetX;
+ }
+ return getBurnInOffset(mBurnInPreventionOffsetX, true /* xAxis */);
}
public static class Result {
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 0b3fd16..093f57a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -196,7 +196,8 @@
new MyOnHeadsUpChangedListener();
private final HeightListener mHeightListener = new HeightListener();
private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
- private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
+ @VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
+ new StatusBarStateListener();
private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
private final BiometricUnlockController mBiometricUnlockController;
private final NotificationPanelView mView;
@@ -280,37 +281,12 @@
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
- if (mDisabledUdfpsController == null
- && mAuthController.getUdfpsRegion() != null
- && mAuthController.isUdfpsEnrolled(
- KeyguardUpdateMonitor.getCurrentUser())) {
- mLayoutInflater.inflate(R.layout.disabled_udfps_view, mView);
- mDisabledUdfpsController = new DisabledUdfpsController(
- mView.findViewById(R.id.disabled_udfps_view),
- mStatusBarStateController,
- mUpdateMonitor,
- mAuthController,
- mStatusBarKeyguardViewManager);
- mDisabledUdfpsController.init();
+ if (showing) {
+ updateDisabledUdfpsController();
}
}
};
- final KeyguardUserSwitcherController.KeyguardUserSwitcherListener
- mKeyguardUserSwitcherListener =
- new KeyguardUserSwitcherController.KeyguardUserSwitcherListener() {
- @Override
- public void onKeyguardUserSwitcherChanged(boolean open) {
- if (mKeyguardUserSwitcherController == null) {
- updateUserSwitcherVisibility(false);
- } else if (!mKeyguardUserSwitcherController.isSimpleUserSwitcher()) {
- updateUserSwitcherVisibility(open
- && mKeyguardStateController.isShowing()
- && !mKeyguardStateController.isKeyguardFadingAway());
- }
- }
- };
-
private final LayoutInflater mLayoutInflater;
private final PowerManager mPowerManager;
private final AccessibilityManager mAccessibilityManager;
@@ -338,7 +314,6 @@
private KeyguardAffordanceHelper mAffordanceHelper;
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
- private boolean mKeyguardUserSwitcherIsShowing;
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
private KeyguardStatusBarView mKeyguardStatusBar;
private ViewGroup mBigClockContainer;
@@ -383,6 +358,7 @@
private ValueAnimator mQsExpansionAnimator;
private FlingAnimationUtils mFlingAnimationUtils;
private int mStatusBarMinHeight;
+ private int mStatusBarHeaderHeightKeyguard;
private int mNotificationsHeaderCollideDistance;
private float mEmptyDragAmount;
private float mDownX;
@@ -473,7 +449,7 @@
private ArrayList<Consumer<ExpandableNotificationRow>>
mTrackingHeadsUpListeners =
new ArrayList<>();
- private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
+ private Runnable mVerticalTranslationListener;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private int mPanelAlpha;
@@ -781,6 +757,8 @@
.setMaxLengthSeconds(0.4f).build();
mStatusBarMinHeight = mResources.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
+ mStatusBarHeaderHeightKeyguard = mResources.getDimensionPixelSize(
+ R.dimen.status_bar_header_height_keyguard);
mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize(
R.dimen.header_notifications_collide_distance);
@@ -817,7 +795,6 @@
// Try to close the switcher so that callbacks are triggered if necessary.
// Otherwise, NPV can get into a state where some of the views are still hidden
mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false);
- mKeyguardUserSwitcherController.removeCallback();
}
mKeyguardQsUserSwitchController = null;
@@ -837,7 +814,6 @@
mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
mKeyguardUserSwitcherController =
userSwitcherComponent.getKeyguardUserSwitcherController();
- mKeyguardUserSwitcherController.setCallback(mKeyguardUserSwitcherListener);
mKeyguardUserSwitcherController.init();
mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
} else {
@@ -867,25 +843,16 @@
public void updateResources() {
int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
- ViewGroup.LayoutParams lp = mQsFrame.getLayoutParams();
- if (lp.width != qsWidth) {
- lp.width = qsWidth;
- mQsFrame.setLayoutParams(lp);
- }
-
int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
- lp = mNotificationStackScrollLayoutController.getLayoutParams();
- if (lp.width != panelWidth) {
- lp.width = panelWidth;
- mNotificationStackScrollLayoutController.setLayoutParams(lp);
- }
- // In order to change the constraints at runtime, all children of the Constraint Layout
- // must have ids.
+ // To change the constraints at runtime, all children of the ConstraintLayout must have ids
ensureAllViewsHaveIds(mNotificationContainerParent);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(mNotificationContainerParent);
if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) {
+ // width = 0 to take up all available space within constraints
+ qsWidth = 0;
+ panelWidth = 0;
constraintSet.connect(R.id.qs_frame, END, R.id.qs_edge_guideline, END);
constraintSet.connect(
R.id.notification_stack_scroller, START,
@@ -894,6 +861,8 @@
constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
}
+ constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
+ constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
constraintSet.applyTo(mNotificationContainerParent);
}
@@ -1085,7 +1054,7 @@
int totalHeight = mView.getHeight();
int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
- int userSwitcherPreferredY = mStatusBarMinHeight;
+ int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
@@ -1094,7 +1063,8 @@
? mKeyguardQsUserSwitchController.getUserIconHeight()
: (mKeyguardUserSwitcherController != null
? mKeyguardUserSwitcherController.getUserIconHeight() : 0);
- mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
+ mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
+ totalHeight - bottomPadding,
mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
getExpandedFraction(),
totalHeight,
@@ -1852,7 +1822,7 @@
}
}
- private void setQsExpanded(boolean expanded) {
+ @VisibleForTesting void setQsExpanded(boolean expanded) {
boolean changed = mQsExpanded != expanded;
if (changed) {
mQsExpanded = expanded;
@@ -1955,8 +1925,10 @@
private void updateQsState() {
mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded);
mNotificationStackScrollLayoutController.setScrollingEnabled(
- mBarState != KEYGUARD && (!mQsExpanded
- || mQsExpansionFromOverscroll));
+ mBarState != KEYGUARD
+ && (!mQsExpanded
+ || mQsExpansionFromOverscroll
+ || Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)));
if (mKeyguardUserSwitcherController != null && mQsExpanded
&& !mStackScrollerOverscrolling) {
@@ -2236,13 +2208,16 @@
@Override
protected boolean canCollapsePanelOnTouch() {
- if (!isInSettings()) {
- return mBarState == KEYGUARD
- || mIsPanelCollapseOnQQS
- || mNotificationStackScrollLayoutController.isScrolledToBottom();
- } else {
+ if (!isInSettings() && mBarState == KEYGUARD) {
return true;
}
+
+ if (mNotificationStackScrollLayoutController.isScrolledToBottom()) {
+ return true;
+ }
+
+ return !Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)
+ && (isInSettings() || mIsPanelCollapseOnQQS);
}
@Override
@@ -2920,7 +2895,8 @@
* @param x the x-coordinate the touch event
*/
protected void updateHorizontalPanelPosition(float x) {
- if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth()) {
+ if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth()
+ || Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) {
resetHorizontalPanelPosition();
return;
}
@@ -2948,9 +2924,8 @@
protected void setHorizontalPanelTranslation(float translation) {
mNotificationStackScrollLayoutController.setTranslationX(translation);
mQsFrame.setTranslationX(translation);
- int size = mVerticalTranslationListener.size();
- for (int i = 0; i < size; i++) {
- mVerticalTranslationListener.get(i).run();
+ if (mVerticalTranslationListener != null) {
+ mVerticalTranslationListener.run();
}
}
@@ -3243,12 +3218,8 @@
mTrackingHeadsUpListeners.remove(listener);
}
- public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
- mVerticalTranslationListener.add(verticalTranslationListener);
- }
-
- public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
- mVerticalTranslationListener.remove(verticalTranslationListener);
+ public void setVerticalTranslationListener(Runnable verticalTranslationListener) {
+ mVerticalTranslationListener = verticalTranslationListener;
}
public void setHeadsUpAppearanceController(
@@ -3538,31 +3509,22 @@
return false;
}
- private void updateUserSwitcherVisibility(boolean open) {
- // Do not update if previously called with the same state.
- if (mKeyguardUserSwitcherIsShowing == open) {
- return;
- }
- mKeyguardUserSwitcherIsShowing = open;
-
- if (open) {
- animateKeyguardStatusBarOut();
- mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
- mBarState,
- true /* keyguardFadingAway */,
- true /* goingToFullShade */,
- mBarState);
- setKeyguardBottomAreaVisibility(mBarState, true);
- mNotificationContainerParent.setVisibility(View.GONE);
- } else {
- animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
- StatusBarState.KEYGUARD,
- false,
- false,
- StatusBarState.SHADE_LOCKED);
- setKeyguardBottomAreaVisibility(mBarState, false);
- mNotificationContainerParent.setVisibility(View.VISIBLE);
+ private void updateDisabledUdfpsController() {
+ final boolean udfpsEnrolled = mAuthController.getUdfpsRegion() != null
+ && mAuthController.isUdfpsEnrolled(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (mDisabledUdfpsController == null && udfpsEnrolled) {
+ mLayoutInflater.inflate(R.layout.disabled_udfps_view, mView);
+ mDisabledUdfpsController = new DisabledUdfpsController(
+ mView.findViewById(R.id.disabled_udfps_view),
+ mStatusBarStateController,
+ mUpdateMonitor,
+ mAuthController,
+ mStatusBarKeyguardViewManager);
+ mDisabledUdfpsController.init();
+ } else if (mDisabledUdfpsController != null && !udfpsEnrolled) {
+ mDisabledUdfpsController.destroy();
+ mDisabledUdfpsController = null;
}
}
@@ -3615,6 +3577,10 @@
NotificationStackScrollLayout.OnOverscrollTopChangedListener {
@Override
public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
+ // When in split shade, overscroll shouldn't carry through to QS
+ if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) {
+ return;
+ }
cancelQsAnimation();
if (!mQsExpansionEnabled) {
amount = 0f;
@@ -3981,6 +3947,7 @@
FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mConfigurationController.addCallback(mConfigurationListener);
+ updateDisabledUdfpsController();
mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
// Theme might have changed between inflating this view and attaching it to the
// window, so
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index e394ebc..0c9ed66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -18,14 +18,12 @@
import android.app.Fragment;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
-import androidx.annotation.DimenRes;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.systemui.R;
@@ -83,22 +81,6 @@
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- reloadWidth(mQsFrame, R.dimen.qs_panel_width);
- reloadWidth(mStackScroller, R.dimen.notification_panel_width);
- }
-
- /**
- * Loads the given width resource and sets it on the given View.
- */
- private void reloadWidth(View view, @DimenRes int width) {
- LayoutParams params = (LayoutParams) view.getLayoutParams();
- params.width = getResources().getDimensionPixelSize(width);
- view.setLayoutParams(params);
- }
-
- @Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mBottomPadding = insets.getStableInsetBottom();
setPadding(0, 0, 0, mBottomPadding);
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 041a97e..b25fced 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -78,6 +78,7 @@
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -276,7 +277,8 @@
public static final boolean DEBUG = false;
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
- public static final boolean DEBUG_GESTURES = false;
+ public static final boolean DEBUG_GESTURES = Build.IS_DEBUGGABLE; // TODO(b/178277858)
+ public static final boolean DEBUG_GESTURES_VERBOSE = true;
public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
public static final boolean DEBUG_CAMERA_LIFT = false;
@@ -456,9 +458,7 @@
private final DisplayMetrics mDisplayMetrics;
// XXX: gesture research
- private final GestureRecorder mGestureRec = DEBUG_GESTURES
- ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
- : null;
+ private GestureRecorder mGestureRec = null;
private final ScreenPinningRequest mScreenPinningRequest;
@@ -856,6 +856,10 @@
mActivityIntentHelper = new ActivityIntentHelper(mContext);
DateTimeView.setReceiverHandler(timeTickHandler);
+
+ if (DEBUG_GESTURES) {
+ mGestureRec = new GestureRecorder(mContext.getCacheDir() + "/statusbar_gestures.dat");
+ }
}
@Override
@@ -2267,7 +2271,7 @@
public boolean interceptTouchEvent(MotionEvent event) {
if (DEBUG_GESTURES) {
- if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
+ if (DEBUG_GESTURES_VERBOSE || event.getActionMasked() != MotionEvent.ACTION_MOVE) {
EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
event.getActionMasked(), (int) event.getX(), (int) event.getY(),
mDisabled1, mDisabled2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index b76e451..8845a05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -19,9 +19,13 @@
import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.database.DataSetObserver;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.UserHandle;
@@ -50,7 +54,6 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.ViewController;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import javax.inject.Inject;
@@ -73,9 +76,10 @@
private final KeyguardUserAdapter mAdapter;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private WeakReference<KeyguardUserSwitcherListener> mKeyguardUserSwitcherCallback;
protected final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
+ private ObjectAnimator mBgAnimator;
+ private final KeyguardUserSwitcherScrim mBackground;
// Child views of KeyguardUserSwitcherView
private KeyguardUserSwitcherListView mListView;
@@ -171,6 +175,7 @@
mUserSwitcherController, this);
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
keyguardStateController, dozeParameters);
+ mBackground = new KeyguardUserSwitcherScrim(context);
}
@Override
@@ -204,6 +209,9 @@
mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mScreenLifecycle.addObserver(mScreenObserver);
+ mView.addOnLayoutChangeListener(mBackground);
+ mView.setBackground(mBackground);
+ mBackground.setAlpha(0);
}
@Override
@@ -217,6 +225,9 @@
mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mScreenLifecycle.removeObserver(mScreenObserver);
+ mView.removeOnLayoutChangeListener(mBackground);
+ mView.setBackground(null);
+ mBackground.setAlpha(0);
}
/**
@@ -338,6 +349,13 @@
animate);
PropertyAnimator.setProperty(mListView, AnimatableProperty.TRANSLATION_X, -Math.abs(x),
ANIMATION_PROPERTIES, animate);
+
+ Rect r = new Rect();
+ mListView.getDrawingRect(r);
+ mView.offsetDescendantRectToMyCoords(mListView, r);
+ mBackground.setGradientCenter(
+ (int) (mListView.getTranslationX() + r.left + r.width() / 2),
+ (int) (mListView.getTranslationY() + r.top + r.height() / 2));
}
/**
@@ -372,49 +390,52 @@
}
/**
- * Remove the callback if it exists.
- */
- public void removeCallback() {
- if (DEBUG) Log.d(TAG, "removeCallback");
- mKeyguardUserSwitcherCallback = null;
- }
-
- /**
- * Register to receive notifications about keyguard user switcher state
- * (see {@link KeyguardUserSwitcherListener}.
- *
- * Only one callback can be used at a time.
- *
- * @param callback The callback to register
- */
- public void setCallback(KeyguardUserSwitcherListener callback) {
- if (DEBUG) Log.d(TAG, "setCallback");
- mKeyguardUserSwitcherCallback = new WeakReference<>(callback);
- }
-
- /**
- * If user switcher state changes, notifies all {@link KeyguardUserSwitcherListener}.
- * Switcher state is updatd before animations finish.
+ * NOTE: switcher state is updated before animations finish.
*
* @param animate true to animate transition. The user switcher state (i.e.
* {@link #isUserSwitcherOpen()}) is updated before animation is finished.
*/
private void setUserSwitcherOpened(boolean open, boolean animate) {
- boolean wasOpen = mUserSwitcherOpen;
if (DEBUG) {
- Log.d(TAG, String.format("setUserSwitcherOpened: %b -> %b (animate=%b)", wasOpen,
- open, animate));
+ Log.d(TAG,
+ String.format("setUserSwitcherOpened: %b -> %b (animate=%b)",
+ mUserSwitcherOpen, open, animate));
}
mUserSwitcherOpen = open;
- if (mUserSwitcherOpen != wasOpen) {
- notifyUserSwitcherStateChanged();
- }
updateVisibilities(animate);
}
private void updateVisibilities(boolean animate) {
if (DEBUG) Log.d(TAG, String.format("updateVisibilities: animate=%b", animate));
mEndGuestButton.animate().cancel();
+ if (mBgAnimator != null) {
+ mBgAnimator.cancel();
+ }
+
+ if (mUserSwitcherOpen) {
+ mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
+ mBgAnimator.setDuration(400);
+ mBgAnimator.setInterpolator(Interpolators.ALPHA_IN);
+ mBgAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBgAnimator = null;
+ }
+ });
+ mBgAnimator.start();
+ } else {
+ mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 255, 0);
+ mBgAnimator.setDuration(400);
+ mBgAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+ mBgAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBgAnimator = null;
+ }
+ });
+ mBgAnimator.start();
+ }
+
if (mUserSwitcherOpen && mCurrentUserIsGuest) {
// Show the "End guest session" button
mEndGuestButton.setVisibility(View.VISIBLE);
@@ -459,34 +480,6 @@
return mUserSwitcherOpen;
}
- private void notifyUserSwitcherStateChanged() {
- if (DEBUG) {
- Log.d(TAG, String.format("notifyUserSwitcherStateChanged: mUserSwitcherOpen=%b",
- mUserSwitcherOpen));
- }
- if (mKeyguardUserSwitcherCallback != null) {
- KeyguardUserSwitcherListener cb = mKeyguardUserSwitcherCallback.get();
- if (cb != null) {
- cb.onKeyguardUserSwitcherChanged(mUserSwitcherOpen);
- }
- }
- }
-
- /**
- * Callback for keyguard user switcher state information
- */
- public interface KeyguardUserSwitcherListener {
-
- /**
- * Called when the keyguard enters or leaves user switcher mode. This will be called
- * before the animations are finished.
- *
- * @param open if true, keyguard is showing the user switcher or transitioning from/to user
- * switcher mode.
- */
- void onKeyguardUserSwitcherChanged(boolean open);
- }
-
static class KeyguardUserAdapter extends
UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
index 49f5bcd..1d9d33d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
@@ -26,7 +26,6 @@
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
-import android.util.LayoutDirection;
import android.view.View;
import com.android.systemui.R;
@@ -38,13 +37,14 @@
implements View.OnLayoutChangeListener {
private static final float OUTER_EXTENT = 2.5f;
- private static final float INNER_EXTENT = 0.75f;
+ private static final float INNER_EXTENT = 0.25f;
private int mDarkColor;
- private int mTop;
private int mAlpha = 255;
private Paint mRadialGradientPaint = new Paint();
- private int mLayoutWidth;
+ private int mCircleX;
+ private int mCircleY;
+ private int mSize;
public KeyguardUserSwitcherScrim(Context context) {
mDarkColor = context.getColor(
@@ -53,14 +53,11 @@
@Override
public void draw(Canvas canvas) {
- boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
+ if (mAlpha == 0) {
+ return;
+ }
Rect bounds = getBounds();
- float width = bounds.width() * OUTER_EXTENT;
- float height = (mTop + bounds.height()) * OUTER_EXTENT;
- canvas.translate(0, -mTop);
- canvas.scale(1, height / width);
- canvas.drawRect(isLtr ? bounds.right - width : 0, 0,
- isLtr ? bounds.right : bounds.left + width, width, mRadialGradientPaint);
+ canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.bottom, mRadialGradientPaint);
}
@Override
@@ -88,24 +85,36 @@
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
- mLayoutWidth = right - left;
- mTop = top;
+ int width = right - left;
+ int height = bottom - top;
+ mSize = Math.max(width, height);
updatePaint();
}
}
private void updatePaint() {
- if (mLayoutWidth == 0) {
+ if (mSize == 0) {
return;
}
- float radius = mLayoutWidth * OUTER_EXTENT;
- boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
+ float outerRadius = mSize * OUTER_EXTENT;
mRadialGradientPaint.setShader(
- new RadialGradient(isLtr ? mLayoutWidth : 0, 0, radius,
+ new RadialGradient(mCircleX, mCircleY, outerRadius,
new int[] { Color.argb(
(int) (Color.alpha(mDarkColor) * mAlpha / 255f), 0, 0, 0),
Color.TRANSPARENT },
- new float[] { Math.max(0f, mLayoutWidth * INNER_EXTENT / radius), 1f },
+ new float[] { Math.max(0f, INNER_EXTENT / OUTER_EXTENT), 1f },
Shader.TileMode.CLAMP));
}
+
+ /**
+ * Sets the center of the radial gradient used as a background
+ *
+ * @param x
+ * @param y
+ */
+ public void setGradientCenter(int x, int y) {
+ mCircleX = x;
+ mCircleY = y;
+ updatePaint();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index bdf2b0c..37a763b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -22,12 +22,10 @@
import android.os.ServiceManager;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar;
import javax.inject.Inject;
@@ -36,11 +34,6 @@
/**
* Status bar implementation for "large screen" products that mostly present no on-screen nav.
* Serves as a collection of UI components, rather than showing its own UI.
- * The following is the list of elements that constitute the TV-specific status bar:
- * <ul>
- * <li> {@link AudioRecordingDisclosureBar} - shown whenever applications are conducting audio
- * recording, discloses the responsible applications </li>
- * </ul>
*/
@SysUISingleton
public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
@@ -66,11 +59,6 @@
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
-
- if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) {
- // Creating AudioRecordingDisclosureBar and just letting it run
- new AudioRecordingDisclosureBar(mContext);
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java
deleted file mode 100644
index bbab625..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tv.micdisclosure;
-
-import android.content.Context;
-
-import java.util.Set;
-
-/**
- * A base class for implementing observers for different kinds of activities related to audio
- * recording. These observers are to be initialized by {@link AudioRecordingDisclosureBar} and to
- * report back to it.
- */
-abstract class AudioActivityObserver {
-
- interface OnAudioActivityStateChangeListener {
- void onAudioActivityStateChange(boolean active, String packageName);
- }
-
- final Context mContext;
-
- final OnAudioActivityStateChangeListener mListener;
-
- AudioActivityObserver(Context context, OnAudioActivityStateChangeListener listener) {
- mContext = context;
- mListener = listener;
- }
-
- abstract void start();
-
- abstract void stop();
-
- abstract Set<String> getActivePackages();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
deleted file mode 100644
index c9d1b71..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tv.micdisclosure;
-
-import static android.provider.DeviceConfig.NAMESPACE_PRIVACY;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.annotation.IntDef;
-import android.annotation.UiThread;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.provider.DeviceConfig;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.tv.TvStatusBar;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A component of {@link TvStatusBar} responsible for notifying the user whenever an application is
- * recording audio.
- *
- * @see TvStatusBar
- */
-public class AudioRecordingDisclosureBar implements
- AudioActivityObserver.OnAudioActivityStateChangeListener {
- private static final String TAG = "AudioRecordingDisclosure";
- static final boolean DEBUG = false;
-
- // This title is used to test the microphone disclosure indicator in
- // CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest
- private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator";
-
- private static final String ENABLED_FLAG = "mic_disclosure_enabled";
- private static final String EXEMPT_PACKAGES_LIST = "mic_disclosure_exempt_packages";
- private static final String FORCED_PACKAGES_LIST = "mic_disclosure_forced_packages";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"STATE_"}, value = {
- STATE_STOPPED,
- STATE_NOT_SHOWN,
- STATE_APPEARING,
- STATE_SHOWN,
- STATE_DISAPPEARING
- })
- public @interface State {}
-
- private static final int STATE_STOPPED = -1;
- private static final int STATE_NOT_SHOWN = 0;
- private static final int STATE_APPEARING = 1;
- private static final int STATE_SHOWN = 2;
- private static final int STATE_DISAPPEARING = 3;
-
- private static final int ANIMATION_DURATION_MS = 200;
-
- private final Context mContext;
- private boolean mIsEnabled;
-
- private View mIndicatorView;
- private boolean mViewAndWindowAdded;
- private ObjectAnimator mAnimator;
-
- @State private int mState = STATE_STOPPED;
-
- /**
- * Array of the observers that monitor different aspects of the system, such as AppOps and
- * microphone foreground services
- */
- private AudioActivityObserver[] mAudioActivityObservers;
- /**
- * Set of applications for which we make an exception and do not show the indicator. This gets
- * populated once - in {@link #AudioRecordingDisclosureBar(Context)}.
- */
- private final Set<String> mExemptPackages = new ArraySet<>();
-
- public AudioRecordingDisclosureBar(Context context) {
- mContext = context;
-
- // Load configs
- reloadExemptPackages();
-
- mIsEnabled = DeviceConfig.getBoolean(NAMESPACE_PRIVACY, ENABLED_FLAG, true);
- // Start if enabled
- if (mIsEnabled) {
- start();
- }
-
- // Set up a config change listener
- DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_PRIVACY, mContext.getMainExecutor(),
- mConfigChangeListener);
- }
-
- private void reloadExemptPackages() {
- mExemptPackages.clear();
- mExemptPackages.addAll(Arrays.asList(mContext.getResources().getStringArray(
- R.array.audio_recording_disclosure_exempt_apps)));
- mExemptPackages.addAll(
- splitByComma(
- DeviceConfig.getString(NAMESPACE_PRIVACY, EXEMPT_PACKAGES_LIST, null)));
- mExemptPackages.removeAll(
- splitByComma(
- DeviceConfig.getString(NAMESPACE_PRIVACY, FORCED_PACKAGES_LIST, null)));
- }
-
- @UiThread
- private void start() {
- if (mState != STATE_STOPPED) {
- return;
- }
- mState = STATE_NOT_SHOWN;
-
- if (mAudioActivityObservers == null) {
- mAudioActivityObservers = new AudioActivityObserver[]{
- new RecordAudioAppOpObserver(mContext, this),
- new MicrophoneForegroundServicesObserver(mContext, this),
- };
- }
-
- for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) {
- mAudioActivityObservers[i].start();
- }
- }
-
- @UiThread
- private void stop() {
- if (mState == STATE_STOPPED) {
- return;
- }
- mState = STATE_STOPPED;
-
- for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) {
- mAudioActivityObservers[i].stop();
- }
-
- // Remove the view if shown.
- if (mState != STATE_NOT_SHOWN) {
- removeIndicatorView();
- }
- }
-
- @UiThread
- @Override
- public void onAudioActivityStateChange(boolean active, String packageName) {
- if (DEBUG) {
- Log.d(TAG,
- "onAudioActivityStateChange, packageName=" + packageName + ", active="
- + active);
- }
-
- if (mExemptPackages.contains(packageName)) {
- if (DEBUG) Log.d(TAG, " - exempt package: ignoring");
- return;
- }
-
- if (active) {
- showIfNeeded();
- } else {
- hideIndicatorIfNeeded();
- }
- }
-
- @UiThread
- private void hideIndicatorIfNeeded() {
- // If STOPPED, NOT_SHOWN or DISAPPEARING - nothing else for us to do here.
- if (mState != STATE_SHOWN && mState != STATE_APPEARING) return;
-
- if (hasActiveRecorders()) {
- return;
- }
-
- if (mViewAndWindowAdded) {
- mState = STATE_DISAPPEARING;
- animateDisappearance();
- } else {
- // Appearing animation has not started yet, as we were still waiting for the View to be
- // laid out.
- mState = STATE_NOT_SHOWN;
- removeIndicatorView();
- }
- }
-
- @UiThread
- private void showIfNeeded() {
- // If STOPPED, SHOWN or APPEARING - nothing else for us to do here.
- if (mState != STATE_NOT_SHOWN && mState != STATE_DISAPPEARING) return;
-
- if (DEBUG) Log.d(TAG, "Showing indicator");
-
- final int prevState = mState;
- mState = STATE_APPEARING;
-
- if (prevState == STATE_DISAPPEARING) {
- animateAppearance();
- return;
- }
-
- // Inflate the indicator view
- mIndicatorView = LayoutInflater.from(mContext).inflate(
- R.layout.tv_audio_recording_indicator, null);
-
- // 1. Set alpha to 0.
- // 2. Wait until the window is shown and the view is laid out.
- // 3. Start a "fade in" (alpha) animation.
- mIndicatorView.setAlpha(0f);
- mIndicatorView
- .getViewTreeObserver()
- .addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- // State could have changed to NOT_SHOWN (if all the recorders are
- // already gone) to STOPPED (if the indicator was disabled)
- if (mState != STATE_APPEARING) return;
-
- mViewAndWindowAdded = true;
- // Remove the observer
- mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
- this);
-
- animateAppearance();
- }
- });
-
- final boolean isLtr = mContext.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR;
- final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
- WRAP_CONTENT,
- WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT);
- layoutParams.gravity = Gravity.TOP | (isLtr ? Gravity.RIGHT : Gravity.LEFT);
- layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
- layoutParams.packageName = mContext.getPackageName();
- final WindowManager windowManager = (WindowManager) mContext.getSystemService(
- Context.WINDOW_SERVICE);
- windowManager.addView(mIndicatorView, layoutParams);
- }
-
-
- private void animateAppearance() {
- animateAlphaTo(1f);
- }
-
- private void animateDisappearance() {
- animateAlphaTo(0f);
- }
-
- private void animateAlphaTo(final float endValue) {
- if (mAnimator == null) {
- if (DEBUG) Log.d(TAG, "set up animator");
-
- mAnimator = new ObjectAnimator();
- mAnimator.setTarget(mIndicatorView);
- mAnimator.setProperty(View.ALPHA);
- mAnimator.addListener(new AnimatorListenerAdapter() {
- boolean mCancelled;
-
- @Override
- public void onAnimationStart(Animator animation, boolean isReverse) {
- if (DEBUG) Log.d(TAG, "AnimatorListenerAdapter#onAnimationStart");
- mCancelled = false;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- if (DEBUG) Log.d(TAG, "AnimatorListenerAdapter#onAnimationCancel");
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (DEBUG) Log.d(TAG, "AnimatorListenerAdapter#onAnimationEnd");
- // When ValueAnimator#cancel() is called it always calls onAnimationCancel(...)
- // and then onAnimationEnd(...). We, however, only want to proceed here if the
- // animation ended "naturally".
- if (!mCancelled) {
- onAnimationFinished();
- }
- }
- });
- } else if (mAnimator.isRunning()) {
- if (DEBUG) Log.d(TAG, "cancel running animation");
- mAnimator.cancel();
- }
-
- final float currentValue = mIndicatorView.getAlpha();
- if (DEBUG) Log.d(TAG, "animate alpha to " + endValue + " from " + currentValue);
-
- mAnimator.setDuration((int) (Math.abs(currentValue - endValue) * ANIMATION_DURATION_MS));
- mAnimator.setFloatValues(endValue);
- mAnimator.start();
- }
-
- private void onAnimationFinished() {
- if (DEBUG) Log.d(TAG, "onAnimationFinished");
-
- if (mState == STATE_APPEARING) {
- mState = STATE_SHOWN;
- } else if (mState == STATE_DISAPPEARING) {
- removeIndicatorView();
- mState = STATE_NOT_SHOWN;
- }
- }
-
- private boolean hasActiveRecorders() {
- for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) {
- for (String activePackage : mAudioActivityObservers[index].getActivePackages()) {
- if (mExemptPackages.contains(activePackage)) continue;
- return true;
- }
- }
- return false;
- }
-
- private void removeIndicatorView() {
- if (DEBUG) Log.d(TAG, "removeIndicatorView");
-
- final WindowManager windowManager = (WindowManager) mContext.getSystemService(
- Context.WINDOW_SERVICE);
- windowManager.removeView(mIndicatorView);
-
- mIndicatorView = null;
- mAnimator = null;
-
- mViewAndWindowAdded = false;
- }
-
- private static List<String> splitByComma(String string) {
- return TextUtils.isEmpty(string) ? Collections.emptyList() : Arrays.asList(
- string.split(","));
- }
-
- private final DeviceConfig.OnPropertiesChangedListener mConfigChangeListener =
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- reloadExemptPackages();
-
- // Check if was enabled/disabled
- if (mIsEnabled != properties.getBoolean(ENABLED_FLAG, true)) {
- mIsEnabled = !mIsEnabled;
- if (mIsEnabled) {
- start();
- } else {
- stop();
- }
- }
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java
deleted file mode 100644
index 8caf95f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tv.micdisclosure;
-
-import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
-
-import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG;
-
-import android.annotation.UiThread;
-import android.app.ActivityManager;
-import android.app.IActivityManager;
-import android.app.IProcessObserver;
-import android.content.Context;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * The purpose of these class is to detect packages that are running foreground services of type
- * 'microphone' and to report back to {@link AudioRecordingDisclosureBar}.
- */
-class MicrophoneForegroundServicesObserver extends AudioActivityObserver {
- private static final String TAG = "MicrophoneForegroundServicesObserver";
-
- private IActivityManager mActivityManager;
- /**
- * A dictionary that maps PIDs to the package names. We only keep track of the PIDs that are
- * "active" (those that are running FGS with FOREGROUND_SERVICE_TYPE_MICROPHONE flag).
- */
- private final SparseArray<String[]> mPidToPackages = new SparseArray<>();
- /**
- * A dictionary that maps "active" packages to the number of the "active" processes associated
- * with those packages. We really only need this in case when one application is running in
- * multiple processes, so that we don't lose track of the package when one of its "active"
- * processes ceases, while others remain "active".
- */
- private final Map<String, Integer> mPackageToProcessCount = new ArrayMap<>();
-
- MicrophoneForegroundServicesObserver(Context context,
- OnAudioActivityStateChangeListener listener) {
- super(context, listener);
- }
-
- @Override
- void start() {
- mActivityManager = ActivityManager.getService();
- try {
- mActivityManager.registerProcessObserver(mProcessObserver);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't register process observer", e);
- }
- }
-
- @Override
- void stop() {
- try {
- mActivityManager.unregisterProcessObserver(mProcessObserver);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't unregister process observer", e);
- }
- mActivityManager = null;
- mPackageToProcessCount.clear();
- }
-
- @Override
- Set<String> getActivePackages() {
- return mPackageToProcessCount.keySet();
- }
-
- @UiThread
- private void onProcessForegroundServicesChanged(int pid, boolean hasMicFgs) {
- final String[] changedPackages;
- if (hasMicFgs) {
- if (mPidToPackages.contains(pid)) {
- // We are already tracking this pid - ignore.
- changedPackages = null;
- } else {
- changedPackages = getPackageNames(pid);
- mPidToPackages.append(pid, changedPackages);
- }
- } else {
- changedPackages = mPidToPackages.removeReturnOld(pid);
- }
-
- if (changedPackages == null) {
- return;
- }
-
- for (int index = changedPackages.length - 1; index >= 0; index--) {
- final String packageName = changedPackages[index];
- int processCount = mPackageToProcessCount.getOrDefault(packageName, 0);
- final boolean shouldNotify;
- if (hasMicFgs) {
- processCount++;
- shouldNotify = processCount == 1;
- } else {
- processCount--;
- shouldNotify = processCount == 0;
- }
- if (processCount > 0) {
- mPackageToProcessCount.put(packageName, processCount);
- } else {
- mPackageToProcessCount.remove(packageName);
- }
- if (shouldNotify) notifyPackageStateChanged(packageName, hasMicFgs);
- }
- }
-
- @UiThread
- private void onProcessDied(int pid) {
- final String[] packages = mPidToPackages.removeReturnOld(pid);
- if (packages == null) {
- // This PID was not active - ignore.
- return;
- }
-
- for (int index = packages.length - 1; index >= 0; index--) {
- final String packageName = packages[index];
- int processCount = mPackageToProcessCount.getOrDefault(packageName, 0);
- if (processCount <= 0) {
- Log.e(TAG, "Bookkeeping error, process count for " + packageName + " is "
- + processCount);
- continue;
- }
- processCount--;
- if (processCount > 0) {
- mPackageToProcessCount.put(packageName, processCount);
- } else {
- mPackageToProcessCount.remove(packageName);
- notifyPackageStateChanged(packageName, false);
- }
- }
- }
-
- @UiThread
- private void notifyPackageStateChanged(String packageName, boolean active) {
- if (DEBUG) {
- Log.d(TAG, (active ? "New microphone fgs detected" : "Microphone fgs is gone")
- + ", package=" + packageName);
- }
-
- mListener.onAudioActivityStateChange(active, packageName);
- }
-
- @UiThread
- private String[] getPackageNames(int pid) {
- final List<ActivityManager.RunningAppProcessInfo> runningApps;
- try {
- runningApps = mActivityManager.getRunningAppProcesses();
- } catch (RemoteException e) {
- Log.d(TAG, "Couldn't get package name for pid=" + pid);
- return null;
- }
- if (runningApps == null) {
- Log.wtf(TAG, "No running apps reported");
- }
- for (ActivityManager.RunningAppProcessInfo app : runningApps) {
- if (app.pid == pid) {
- return app.pkgList;
- }
- }
- return null;
- }
-
- private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
- @Override
- public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {}
-
- @Override
- public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {
- mContext.getMainExecutor().execute(() -> onProcessForegroundServicesChanged(pid,
- (serviceTypes & FOREGROUND_SERVICE_TYPE_MICROPHONE) != 0));
- }
-
- @Override
- public void onProcessDied(int pid, int uid) {
- mContext.getMainExecutor().execute(
- () -> MicrophoneForegroundServicesObserver.this.onProcessDied(pid));
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java
deleted file mode 100644
index 9a2b4a9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tv.micdisclosure;
-
-import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG;
-
-import android.annotation.UiThread;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.util.ArraySet;
-import android.util.Log;
-
-import java.util.Set;
-
-/**
- * The purpose of these class is to detect packages that are conducting audio recording (according
- * to {@link AppOpsManager}) and report this to {@link AudioRecordingDisclosureBar}.
- */
-class RecordAudioAppOpObserver extends AudioActivityObserver implements
- AppOpsManager.OnOpActiveChangedListener {
- private static final String TAG = "RecordAudioAppOpObserver";
-
- /**
- * Set of the applications that currently are conducting audio recording according to {@link
- * AppOpsManager}.
- */
- private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>();
-
- RecordAudioAppOpObserver(Context context, OnAudioActivityStateChangeListener listener) {
- super(context, listener);
- }
-
- @Override
- void start() {
- if (DEBUG) {
- Log.d(TAG, "Start");
- }
-
- // Register AppOpsManager callback
- mContext.getSystemService(AppOpsManager.class)
- .startWatchingActive(
- new String[]{AppOpsManager.OPSTR_RECORD_AUDIO},
- mContext.getMainExecutor(),
- this);
- }
-
- @Override
- void stop() {
- if (DEBUG) {
- Log.d(TAG, "Stop");
- }
-
- // Unregister AppOpsManager callback
- mContext.getSystemService(AppOpsManager.class).stopWatchingActive(this);
-
- // Clean up state
- mActiveAudioRecordingPackages.clear();
- }
-
- @UiThread
- @Override
- Set<String> getActivePackages() {
- return mActiveAudioRecordingPackages;
- }
-
- @UiThread
- @Override
- public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
- if (DEBUG) {
- Log.d(TAG,
- "OP_RECORD_AUDIO active change, active=" + active + ", package="
- + packageName);
- }
-
- if (active) {
- if (mActiveAudioRecordingPackages.add(packageName)) {
- mListener.onAudioActivityStateChange(true, packageName);
- }
- } else {
- if (mActiveAudioRecordingPackages.remove(packageName)) {
- mListener.onAudioActivityStateChange(false, packageName);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index 0a3e833..9effc67 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -94,6 +94,7 @@
*/
static final List<String> THEME_CATEGORIES = Lists.newArrayList(
OVERLAY_CATEGORY_SYSTEM_PALETTE,
+ OVERLAY_CATEGORY_NEUTRAL_PALETTE,
OVERLAY_CATEGORY_ICON_LAUNCHER,
OVERLAY_CATEGORY_SHAPE,
OVERLAY_CATEGORY_FONT,
@@ -107,6 +108,7 @@
@VisibleForTesting
static final Set<String> SYSTEM_USER_CATEGORIES = Sets.newHashSet(
OVERLAY_CATEGORY_SYSTEM_PALETTE,
+ OVERLAY_CATEGORY_NEUTRAL_PALETTE,
OVERLAY_CATEGORY_ACCENT_COLOR,
OVERLAY_CATEGORY_FONT,
OVERLAY_CATEGORY_SHAPE,
@@ -129,8 +131,9 @@
mLauncherPackage = launcherPackage;
mThemePickerPackage = themePickerPackage;
mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
- OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR,
- OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID));
+ OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_NEUTRAL_PALETTE,
+ OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE,
+ OVERLAY_CATEGORY_ICON_ANDROID));
mTargetPackageToCategories.put(SYSUI_PACKAGE,
Sets.newHashSet(OVERLAY_CATEGORY_ICON_SYSUI));
mTargetPackageToCategories.put(SETTINGS_PACKAGE,
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 1f222d8..028cbd0 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -86,7 +86,7 @@
protected static final int PRIMARY = 0;
protected static final int SECONDARY = 1;
- protected static final int NEUTRAL = 1;
+ protected static final int NEUTRAL = 2;
// If lock screen wallpaper colors should also be considered when selecting the theme.
// Doing this has performance impact, given that overlays would need to be swapped when
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index 365cd2a..fab1655 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -187,10 +187,7 @@
mPluginToast.onOrientationChange(orientation);
}
- mDefaultY = mContext.getResources().getDimensionPixelSize(
- mToastStyleEnabled
- ? com.android.systemui.R.dimen.toast_y_offset
- : R.dimen.toast_y_offset);
+ mDefaultY = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
mDefaultGravity =
mContext.getResources().getInteger(R.integer.config_toastDefaultGravity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 78341ed..5b66216 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -43,11 +43,13 @@
private static final String TAG_TUNER = "tuner";
private final DemoModeController mDemoModeController;
+ private final TunerService mTunerService;
@Inject
- TunerActivity(DemoModeController demoModeController) {
+ TunerActivity(DemoModeController demoModeController, TunerService tunerService) {
super();
mDemoModeController = demoModeController;
+ mTunerService = tunerService;
}
protected void onCreate(Bundle savedInstanceState) {
@@ -67,7 +69,7 @@
"com.android.settings.action.DEMO_MODE");
final PreferenceFragment fragment = showDemoMode
? new DemoModeFragment(mDemoModeController)
- : new TunerFragment();
+ : new TunerFragment(mTunerService);
getFragmentManager().beginTransaction().replace(R.id.content_frame,
fragment, TAG_TUNER).commit();
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 4c724ae..989462a 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -15,7 +15,7 @@
*/
package com.android.systemui.tuner;
-import android.app.ActivityManager;
+import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -23,7 +23,6 @@
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Build;
import android.os.Bundle;
-import android.os.UserHandle;
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuInflater;
@@ -56,6 +55,15 @@
private static final int MENU_REMOVE = Menu.FIRST + 1;
+ private final TunerService mTunerService;
+
+ // We are the only ones who ever call this constructor, so don't worry about the warning
+ @SuppressLint("ValidFragment")
+ public TunerFragment(TunerService tunerService) {
+ super();
+ mTunerService = tunerService;
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -124,13 +132,9 @@
getActivity().finish();
return true;
case MENU_REMOVE:
- UserHandle user = new UserHandle(ActivityManager.getCurrentUser());
- TunerService.showResetRequest(getContext(), user, new Runnable() {
- @Override
- public void run() {
- if (getActivity() != null) {
- getActivity().finish();
- }
+ mTunerService.showResetRequest(() -> {
+ if (getActivity() != null) {
+ getActivity().finish();
}
});
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index b67574d..5d09e06 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -15,19 +15,10 @@
package com.android.systemui.tuner;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.UserHandle;
-import android.provider.Settings;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
public abstract class TunerService {
@@ -47,6 +38,16 @@
public abstract void addTunable(Tunable tunable, String... keys);
public abstract void removeTunable(Tunable tunable);
+ /**
+ * Sets the state of the {@link TunerActivity} component for the current user
+ */
+ public abstract void setTunerEnabled(boolean enabled);
+
+ /**
+ * Returns true if the tuner is enabled for the current user.
+ */
+ public abstract boolean isTunerEnabled();
+
public interface Tunable {
void onTuningChanged(String key, String newValue);
}
@@ -55,38 +56,6 @@
mContext = context;
}
- private static Context userContext(Context context, UserHandle user) {
- try {
- return context.createPackageContextAsUser(context.getPackageName(), 0, user);
- } catch (NameNotFoundException e) {
- return context;
- }
- }
-
- /** Enables or disables the tuner for the supplied user. */
- public void setTunerEnabled(UserHandle user, boolean enabled) {
- setTunerEnabled(mContext, user, enabled);
- }
-
- public static final void setTunerEnabled(Context context, UserHandle user, boolean enabled) {
- userContext(context, user).getPackageManager().setComponentEnabledSetting(
- new ComponentName(context, TunerActivity.class),
- enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- }
-
- /** Returns true if the tuner is enabled for the supplied user. */
- public boolean isTunerEnabled(UserHandle user) {
- return isTunerEnabled(mContext, user);
- }
-
- public static final boolean isTunerEnabled(Context context, UserHandle user) {
- return userContext(context, user).getPackageManager().getComponentEnabledSetting(
- new ComponentName(context, TunerActivity.class))
- == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
- }
-
public static class ClearReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -97,35 +66,7 @@
}
/** */
- public void showResetRequest(UserHandle user, final Runnable onDisabled) {
- showResetRequest(mContext, user, onDisabled);
- }
-
- public static final void showResetRequest(final Context context, UserHandle user,
- final Runnable onDisabled) {
- SystemUIDialog dialog = new SystemUIDialog(context);
- dialog.setShowForAllUsers(true);
- dialog.setMessage(R.string.remove_from_settings_prompt);
- dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel),
- (OnClickListener) null);
- dialog.setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(R.string.guest_exit_guest_dialog_remove), new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // Tell the tuner (in main SysUI process) to clear all its settings.
- context.sendBroadcast(new Intent(TunerService.ACTION_CLEAR));
- // Disable access to tuner.
- TunerService.setTunerEnabled(context, user, false);
- // Make them sit through the warning dialog again.
- Settings.Secure.putInt(context.getContentResolver(),
- TunerFragment.SETTING_SEEN_TUNER_WARNING, 0);
- if (onDisabled != null) {
- onDisabled.run();
- }
- }
- });
- dialog.show();
- }
+ public abstract void showResetRequest(Runnable onDisabled);
public static boolean parseIntegerSwitch(String value, boolean defaultValue) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 027c282b..e9e4380 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -15,8 +15,12 @@
*/
package com.android.systemui.tuner;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
@@ -32,13 +36,14 @@
import com.android.internal.util.ArrayUtils;
import com.android.systemui.DejankUtils;
-import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.leak.LeakDetector;
import java.util.HashSet;
@@ -83,6 +88,7 @@
private int mCurrentUser;
private UserTracker.Callback mCurrentUserTracker;
private UserTracker mUserTracker;
+ private final ComponentName mTunerComponent;
/**
*/
@@ -92,7 +98,6 @@
@Main Handler mainHandler,
LeakDetector leakDetector,
DemoModeController demoModeController,
- BroadcastDispatcher broadcastDispatcher,
UserTracker userTracker) {
super(context);
mContext = context;
@@ -100,6 +105,7 @@
mLeakDetector = leakDetector;
mDemoModeController = demoModeController;
mUserTracker = userTracker;
+ mTunerComponent = new ComponentName(mContext, TunerActivity.class);
for (UserInfo user : UserManager.get(mContext).getUsers()) {
mCurrentUser = user.getUserHandle().getIdentifier();
@@ -142,7 +148,7 @@
}
}
if (oldVersion < 2) {
- setTunerEnabled(mContext, mUserTracker.getUserHandle(), false);
+ setTunerEnabled(false);
}
// 3 Removed because of a revert.
if (oldVersion < 4) {
@@ -269,6 +275,46 @@
}
}
+
+ @Override
+ public void setTunerEnabled(boolean enabled) {
+ mUserTracker.getUserContext().getPackageManager().setComponentEnabledSetting(
+ mTunerComponent,
+ enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP
+ );
+ }
+
+ @Override
+ public boolean isTunerEnabled() {
+ return mUserTracker.getUserContext().getPackageManager().getComponentEnabledSetting(
+ mTunerComponent) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ }
+
+ @Override
+ public void showResetRequest(Runnable onDisabled) {
+ SystemUIDialog dialog = new SystemUIDialog(mContext);
+ dialog.setShowForAllUsers(true);
+ dialog.setMessage(R.string.remove_from_settings_prompt);
+ dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mContext.getString(R.string.cancel),
+ (DialogInterface.OnClickListener) null);
+ dialog.setButton(DialogInterface.BUTTON_POSITIVE,
+ mContext.getString(R.string.guest_exit_guest_dialog_remove), (d, which) -> {
+ // Tell the tuner (in main SysUI process) to clear all its settings.
+ mContext.sendBroadcast(new Intent(TunerService.ACTION_CLEAR));
+ // Disable access to tuner.
+ setTunerEnabled(false);
+ // Make them sit through the warning dialog again.
+ Secure.putInt(mContext.getContentResolver(),
+ TunerFragment.SETTING_SEEN_TUNER_WARNING, 0);
+ if (onDisabled != null) {
+ onDisabled.run();
+ }
+ });
+ dialog.show();
+ }
+
private class Observer extends ContentObserver {
public Observer() {
super(new Handler(Looper.getMainLooper()));
diff --git a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
index 79a197d..a22793b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
@@ -16,15 +16,18 @@
package com.android.systemui.util;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableWrapper;
+import android.graphics.drawable.InsetDrawable;
import android.util.AttributeSet;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.systemui.R;
import org.xmlpull.v1.XmlPullParser;
@@ -45,13 +48,18 @@
* @attr ref R.styleable#AlphaTintDrawableWrapper_tint
* @attr ref R.styleable#AlphaTintDrawableWrapper_alpha
*/
-public class AlphaTintDrawableWrapper extends DrawableWrapper {
+public class AlphaTintDrawableWrapper extends InsetDrawable {
private ColorStateList mTint;
private int[] mThemeAttrs;
/** No-arg constructor used by drawable inflation. */
public AlphaTintDrawableWrapper() {
- super(null);
+ super(null, 0);
+ }
+
+ AlphaTintDrawableWrapper(Drawable drawable, int[] themeAttrs) {
+ super(drawable, 0);
+ mThemeAttrs = themeAttrs;
}
@Override
@@ -74,7 +82,7 @@
public void applyTheme(Theme t) {
super.applyTheme(t);
- if (mThemeAttrs != null) {
+ if (mThemeAttrs != null && t != null) {
final TypedArray a = t.resolveAttributes(mThemeAttrs,
R.styleable.AlphaTintDrawableWrapper);
updateStateFromTypedArray(a);
@@ -92,9 +100,6 @@
}
private void updateStateFromTypedArray(@NonNull TypedArray a) {
- if (a.hasValue(R.styleable.AlphaTintDrawableWrapper_android_drawable)) {
- setDrawable(a.getDrawable(R.styleable.AlphaTintDrawableWrapper_android_drawable));
- }
if (a.hasValue(R.styleable.AlphaTintDrawableWrapper_android_tint)) {
mTint = a.getColorStateList(R.styleable.AlphaTintDrawableWrapper_android_tint);
}
@@ -109,4 +114,57 @@
getDrawable().mutate().setTintList(mTint);
}
}
+
+ @Nullable
+ @Override
+ public ConstantState getConstantState() {
+ return new AlphaTintState(super.getConstantState(), mThemeAttrs, getAlpha(), mTint);
+ }
+
+ static class AlphaTintState extends Drawable.ConstantState {
+
+ private ConstantState mWrappedState;
+ private int[] mThemeAttrs;
+ private int mAlpha;
+ private ColorStateList mColorStateList;
+
+ AlphaTintState(
+ ConstantState wrappedState,
+ int[] themeAttrs,
+ int alpha,
+ ColorStateList colorStateList
+ ) {
+ mWrappedState = wrappedState;
+ mThemeAttrs = themeAttrs;
+ mAlpha = alpha;
+ mColorStateList = colorStateList;
+ }
+
+ @NonNull
+ @Override
+ public Drawable newDrawable() {
+ return newDrawable(null, null);
+ }
+
+ @NonNull
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ DrawableWrapper wrapper = (DrawableWrapper) mWrappedState.newDrawable(res, theme);
+ AlphaTintDrawableWrapper alphaTintDrawableWrapper =
+ new AlphaTintDrawableWrapper(wrapper.getDrawable(), mThemeAttrs);
+ alphaTintDrawableWrapper.setTintList(mColorStateList);
+ alphaTintDrawableWrapper.setAlpha(mAlpha);
+ return alphaTintDrawableWrapper;
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return true;
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return mWrappedState.getChangingConfigurations();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
index 1af2c9f..6aadd10 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -17,15 +17,12 @@
package com.android.systemui.util
import android.content.res.Resources
-import android.content.res.TypedArray
import android.graphics.Canvas
import android.graphics.Path
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.DrawableWrapper
-import android.util.AttributeSet
-import com.android.systemui.R
-import org.xmlpull.v1.XmlPullParser
+import android.graphics.drawable.InsetDrawable
/**
* [DrawableWrapper] to use in the progress of a slider.
@@ -38,9 +35,9 @@
* is meant to be smaller than the rounded corner. The background should have rounded corners that
* are half of the height.
*/
-class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawable) {
-
- constructor() : this(null)
+class RoundedCornerProgressDrawable @JvmOverloads constructor(
+ drawable: Drawable? = null
+) : InsetDrawable(drawable, 0) {
companion object {
private const val MAX_LEVEL = 10000 // Taken from Drawable
@@ -52,35 +49,11 @@
setClipPath(Rect())
}
- override fun inflate(
- r: Resources,
- parser: XmlPullParser,
- attrs: AttributeSet,
- theme: Resources.Theme?
- ) {
- val a = obtainAttributes(r, theme, attrs, R.styleable.RoundedCornerProgressDrawable)
-
- // Inflation will advance the XmlPullParser and AttributeSet.
- super.inflate(r, parser, attrs, theme)
-
- updateStateFromTypedArray(a)
- if (drawable == null) {
- throw IllegalStateException("${this::class.java.simpleName} needs a drawable")
- }
- a.recycle()
- }
-
override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean {
onLevelChange(level)
return super.onLayoutDirectionChanged(layoutDirection)
}
- private fun updateStateFromTypedArray(a: TypedArray) {
- if (a.hasValue(R.styleable.RoundedCornerProgressDrawable_android_drawable)) {
- setDrawable(a.getDrawable(R.styleable.RoundedCornerProgressDrawable_android_drawable))
- }
- }
-
override fun onBoundsChange(bounds: Rect) {
setClipPath(bounds)
super.onBoundsChange(bounds)
@@ -115,4 +88,24 @@
super.draw(canvas)
canvas.restore()
}
+
+ override fun getConstantState(): ConstantState? {
+ // This should not be null as it was created with a state in the constructor.
+ return RoundedCornerState(super.getConstantState()!!)
+ }
+
+ private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() {
+ override fun newDrawable(): Drawable {
+ return newDrawable(null, null)
+ }
+
+ override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable {
+ val wrapper = wrappedState.newDrawable(res, theme) as DrawableWrapper
+ return RoundedCornerProgressDrawable(wrapper.drawable)
+ }
+
+ override fun getChangingConfigurations(): Int {
+ return wrappedState.changingConfigurations
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index 06806d0..6a648bd 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -347,6 +347,7 @@
public void check(long timeoutMs, Consumer<Boolean> callback) {
if (!mSensor.isLoaded()) {
callback.accept(null);
+ return;
}
mCallbacks.add(callback);
if (!mRegistered.getAndSet(true)) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index fba0b00..b9b54fc 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -31,6 +31,7 @@
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.R;
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -97,7 +98,12 @@
@Module
public abstract class WMShellBaseModule {
- private static final boolean ENABLE_SHELL_MAIN_THREAD = false;
+ /**
+ * Returns whether to enable a separate shell thread for the shell features.
+ */
+ private static boolean enableShellMainThread(Context context) {
+ return context.getResources().getBoolean(R.bool.config_enableShellMainThread);
+ }
//
// Shell Concurrency - Components used for managing threading in the Shell and SysUI
@@ -120,8 +126,8 @@
@WMSingleton
@Provides
@ShellMainThread
- public static Handler provideShellMainHandler(@Main Handler sysuiMainHandler) {
- if (ENABLE_SHELL_MAIN_THREAD) {
+ public static Handler provideShellMainHandler(Context context, @Main Handler sysuiMainHandler) {
+ if (enableShellMainThread(context)) {
HandlerThread mainThread = new HandlerThread("wmshell.main");
mainThread.start();
return mainThread.getThreadHandler();
@@ -135,9 +141,9 @@
@WMSingleton
@Provides
@ShellMainThread
- public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler mainHandler,
- @Main ShellExecutor sysuiMainExecutor) {
- if (ENABLE_SHELL_MAIN_THREAD) {
+ public static ShellExecutor provideShellMainExecutor(Context context,
+ @ShellMainThread Handler mainHandler, @Main ShellExecutor sysuiMainExecutor) {
+ if (enableShellMainThread(context)) {
return new HandlerExecutor(mainHandler);
}
return sysuiMainExecutor;
@@ -380,9 +386,9 @@
@WMSingleton
@Provides
static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
- @ShellMainThread ShellExecutor mainExecutor,
+ Context context, @ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor) {
- return new Transitions(organizer, pool, mainExecutor, animExecutor);
+ return new Transitions(organizer, pool, context, mainExecutor, animExecutor);
}
//
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 6c3b37e..ba21afd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -309,17 +309,6 @@
}
@Test
- public void testOverlayPredicate() {
- StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
- 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
- StatusBarNotification sbn_user1_overlay = makeMockSBN(USERID_ONE, "android",
- 0, "AlertWindowNotification", Notification.FLAG_NO_CLEAR);
-
- assertTrue(mFsc.isSystemAlertNotification(sbn_user1_overlay));
- assertFalse(mFsc.isSystemAlertNotification(sbn_user1_app1));
- }
-
- @Test
public void testNoNotifsNorAppOps_noSystemAlertWarningRequired() {
// no notifications nor app op signals that this package/userId requires system alert
// warning
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 0c69ffd..e1ddaad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -53,6 +54,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
+@Ignore
public class ImageWallpaperTest extends SysuiTestCase {
private static final int LOW_BMP_WIDTH = 128;
private static final int LOW_BMP_HEIGHT = 128;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index bc322f7..97cb873 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -69,6 +69,7 @@
@TestableLooper.RunWithLooper
public class AppOpsControllerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
+ private static final String TEST_ATTRIBUTION_NAME = "attribution";
private static final int TEST_UID = UserHandle.getUid(0, 0);
private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
@@ -164,7 +165,7 @@
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mTestableLooper.processAllMessages();
verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
@@ -218,8 +219,8 @@
mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
- TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.OP_FLAG_SELF,
- AppOpsManager.MODE_ALLOWED);
+ TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME,
+ AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
assertEquals(3, mController.getActiveAppOps().size());
}
@@ -230,8 +231,8 @@
mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
- TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.OP_FLAG_SELF,
- AppOpsManager.MODE_ALLOWED);
+ TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME,
+ AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
assertEquals(2,
mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
assertEquals(1,
@@ -262,7 +263,7 @@
public void opNotedScheduledForRemoval() {
mController.setBGHandler(mMockHandler);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong());
}
@@ -274,7 +275,7 @@
mController.onOpActiveChanged(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
assertFalse(mController.getActiveAppOps().isEmpty());
mController.setListening(false);
@@ -288,9 +289,9 @@
mController.setBGHandler(mMockHandler);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
// Only one post to notify subscribers
verify(mMockHandler, times(1)).post(any());
@@ -304,9 +305,9 @@
mController.setBGHandler(mMockHandler);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
// Only one post to notify subscribers
verify(mMockHandler, times(2)).scheduleRemoval(any(), anyLong());
@@ -324,7 +325,7 @@
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
// Check that we "scheduled" the removal. Don't actually schedule until we are ready to
// process messages at a later time.
@@ -353,7 +354,7 @@
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mController.onOpActiveChanged(
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
@@ -382,7 +383,7 @@
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mController.onOpActiveChanged(
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
@@ -400,7 +401,7 @@
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
- AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
+ TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mTestableLooper.processAllMessages();
verify(mCallback).onActiveStateChanged(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index d607727..afe5c0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -41,6 +41,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -74,6 +75,8 @@
private Handler mHandler;
private HandlerThread mHandlerThread;
private DozeUi mDozeUi;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
@Before
public void setUp() throws Exception {
@@ -85,7 +88,8 @@
mHandler = mHandlerThread.getThreadHandler();
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService);
+ mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
+ () -> mStatusBarStateController);
mDozeUi.setDozeMachine(mMachine);
}
@@ -141,7 +145,8 @@
reset(mHost);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService);
+ mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
+ () -> mStatusBarStateController);
mDozeUi.setDozeMachine(mMachine);
// Never animate if display doesn't support it.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
index e23507b..a3221b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
@@ -50,6 +50,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@Ignore
public class EglHelperTest extends SysuiTestCase {
@Spy
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
index 24f3eb2..510b907 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
@@ -33,6 +33,7 @@
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,6 +44,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@Ignore
public class ImageWallpaperRendererTest extends SysuiTestCase {
private WallpaperManager mWpmSpy;
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 d79155c..c8e9396 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.people;
+import static android.app.Notification.CATEGORY_MISSED_CALL;
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;
@@ -113,6 +114,7 @@
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
private static final String GAME_DESCRIPTION = "Playing a game!";
+ private static final CharSequence MISSED_CALL = "Custom missed call message";
private static final String NAME = "username";
private static final Person PERSON = new Person.Builder()
.setName("name")
@@ -346,7 +348,7 @@
.build();
Notification.MessagingStyle.Message lastMessage =
- PeopleSpaceUtils.getLastMessagingStyleMessage(sbn);
+ PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification());
assertThat(lastMessage).isNull();
}
@@ -447,7 +449,7 @@
.build();
Notification.MessagingStyle.Message lastMessage =
- PeopleSpaceUtils.getLastMessagingStyleMessage(sbn);
+ PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification());
assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2);
}
@@ -465,7 +467,7 @@
.setUid(0)
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromNotification(tile, sbn);
+ .augmentTileFromNotification(mContext, tile, sbn);
assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
}
@@ -483,9 +485,8 @@
.setUid(0)
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromNotification(tile, sbn);
+ .augmentTileFromNotification(mContext, tile, sbn);
- assertThat(actual.getNotificationKey()).isEqualTo(null);
assertThat(actual.getNotificationContent()).isEqualTo(null);
}
@@ -498,7 +499,7 @@
.setUid(0)
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromVisibleNotifications(tile,
+ .augmentTileFromVisibleNotifications(mContext, tile,
Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
@@ -513,7 +514,7 @@
.setUid(0)
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromVisibleNotifications(tile,
+ .augmentTileFromVisibleNotifications(mContext, tile,
Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
assertThat(actual.getNotificationContent()).isEqualTo(null);
@@ -528,7 +529,8 @@
.setUid(0)
.build();
List<PeopleSpaceTile> actualList = PeopleSpaceUtils
- .augmentTilesFromVisibleNotifications(List.of(tile), mNotificationEntryManager);
+ .augmentTilesFromVisibleNotifications(
+ mContext, List.of(tile), mNotificationEntryManager);
assertThat(actualList.size()).isEqualTo(1);
assertThat(actualList.get(0).getNotificationContent().toString())
@@ -552,7 +554,7 @@
.setUid(0)
.build();
List<PeopleSpaceTile> actualList = PeopleSpaceUtils
- .augmentTilesFromVisibleNotifications(List.of(tile1, tile2),
+ .augmentTilesFromVisibleNotifications(mContext, List.of(tile1, tile2),
mNotificationEntryManager);
assertThat(actualList.size()).isEqualTo(2);
@@ -763,6 +765,33 @@
}
@Test
+ public void testCreateRemoteViewsWithMissedCallNotification() {
+ PeopleSpaceTile tileWithMissedCallNotification = PERSON_TILE.toBuilder()
+ .setNotificationDataUri(null)
+ .setNotificationCategory(CATEGORY_MISSED_CALL)
+ .setNotificationContent(MISSED_CALL)
+ .build();
+ RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
+ tileWithMissedCallNotification, 0);
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ // 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);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
+ assertEquals(View.GONE, personIconWithStory.getVisibility());
+ // Has status.
+ TextView statusContent = (TextView) result.findViewById(R.id.status);
+ assertEquals(statusContent.getText(), MISSED_CALL);
+ }
+
+
+ @Test
public void testCreateRemoteViewsWithNotificationTemplate() {
PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder()
.setNotificationDataUri(null)
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 9470141..1c8324c 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
@@ -16,6 +16,7 @@
package com.android.systemui.people.widget;
+import static android.app.Notification.CATEGORY_MISSED_CALL;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -167,7 +168,8 @@
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+ StatusBarNotification sbn = createNotification(
+ OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
.setId(1));
@@ -207,7 +209,8 @@
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
- .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setNotification(createMessagingStyleNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false))
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbnWithoutPackageName)
@@ -256,7 +259,8 @@
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+ StatusBarNotification sbn = createNotification(
+ OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
.setId(1));
@@ -276,7 +280,8 @@
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
- .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setNotification(createMessagingStyleNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false))
.setPkg(TEST_PACKAGE_B)
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -295,7 +300,8 @@
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+ StatusBarNotification sbn = createNotification(
+ OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
.setId(1));
@@ -315,7 +321,8 @@
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
- .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setNotification(createMessagingStyleNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
.setPkg(TEST_PACKAGE_B)
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -337,7 +344,8 @@
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
@@ -367,7 +375,8 @@
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
@@ -400,7 +409,8 @@
PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
@@ -417,33 +427,52 @@
}
@Test
- public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile()
+ public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile()
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
- Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
- .setContentTitle("TEST_TITLE")
- .setContentText("TEST_TEXT")
- .setShortcutId(SHORTCUT_ID)
- .build();
- StatusBarNotification sbn = new SbnBuilder()
- .setNotification(notificationWithoutMessagingStyle)
- .setPkg(TEST_PACKAGE_A)
- .setUid(0)
- .build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setSbn(sbn)
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ true))
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, times(1))
.updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
mBundleArgumentCaptor.capture());
- Bundle options = requireNonNull(mBundleArgumentCaptor.getValue());
- assertThat((PeopleSpaceTile) options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE))
- .isEqualTo(PERSON_TILE);
+ Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tile.getNotificationContent())
+ .isEqualTo(mContext.getString(R.string.missed_call));
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile()
+ throws Exception {
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
@@ -453,7 +482,8 @@
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
+ StatusBarNotification sbn = createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
.setId(1));
@@ -483,21 +513,29 @@
return convo;
}
- private Notification createMessagingStyleNotification(String shortcutId) {
- return new Notification.Builder(mContext)
+ private Notification createMessagingStyleNotification(String shortcutId,
+ boolean isMessagingStyle, boolean isMissedCall) {
+ Notification.Builder builder = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
- .setShortcutId(shortcutId)
- .setStyle(new Notification.MessagingStyle(PERSON)
- .addMessage(
- new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
- PERSON))
- )
- .build();
+ .setShortcutId(shortcutId);
+ if (isMessagingStyle) {
+ builder.setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(
+ new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+ PERSON))
+ );
+ }
+ if (isMissedCall) {
+ builder.setCategory(CATEGORY_MISSED_CALL);
+ }
+ return builder.build();
}
- private StatusBarNotification createConversationNotification(String shortcutId) {
- Notification notification = createMessagingStyleNotification(shortcutId);
+ private StatusBarNotification createNotification(String shortcutId,
+ boolean isMessagingStyle, boolean isMissedCall) {
+ Notification notification = createMessagingStyleNotification(
+ shortcutId, isMessagingStyle, isMissedCall);
return new SbnBuilder()
.setNotification(notification)
.setPkg(TEST_PACKAGE_A)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index d3dbe2b..ccd9548 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -25,7 +25,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
-import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlsController
@@ -37,9 +36,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.FeatureFlags
-import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.settings.FakeSettings
@@ -49,7 +46,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
@@ -57,6 +53,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.Optional
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -73,6 +70,7 @@
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var qsLogger: QSLogger
+ @Mock
private lateinit var controlsComponent: ControlsComponent
@Mock
private lateinit var controlsUiController: ControlsUiController
@@ -96,54 +94,64 @@
private lateinit var testableLooper: TestableLooper
private lateinit var tile: DeviceControlsTile
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var lockPatternUtils: LockPatternUtils
- @Mock
private lateinit var secureSettings: SecureSettings
+ private var featureEnabled = true
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ secureSettings = FakeSettings()
`when`(qsHost.context).thenReturn(mContext)
`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+ `when`(controlsController.available).thenReturn(true)
+ `when`(controlsComponent.isEnabled()).thenReturn(true)
+ secureSettings.putInt(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 1)
- controlsComponent = ControlsComponent(
- true,
- mContext,
- { controlsController },
- { controlsUiController },
- { controlsListingController },
- lockPatternUtils,
- keyguardStateController,
- userTracker,
- secureSettings
- )
+ setupControlsComponent()
globalSettings = FakeSettings()
globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 1)
`when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(true)
- `when`(userTracker.userHandle.identifier).thenReturn(0)
-
tile = createTile()
}
+ private fun setupControlsComponent() {
+ `when`(controlsComponent.getControlsController()).thenAnswer {
+ if (featureEnabled) {
+ Optional.of(controlsController)
+ } else {
+ Optional.empty()
+ }
+ }
+
+ `when`(controlsComponent.getControlsListingController()).thenAnswer {
+ if (featureEnabled) {
+ Optional.of(controlsListingController)
+ } else {
+ Optional.empty()
+ }
+ }
+
+ `when`(controlsComponent.getControlsUiController()).thenAnswer {
+ if (featureEnabled) {
+ Optional.of(controlsUiController)
+ } else {
+ Optional.empty()
+ }
+ }
+ }
+
@Test
fun testAvailable() {
- `when`(controlsController.available).thenReturn(true)
assertThat(tile.isAvailable).isTrue()
}
@Test
fun testNotAvailableFeature() {
- `when`(controlsController.available).thenReturn(true)
`when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(false)
assertThat(tile.isAvailable).isFalse()
@@ -151,24 +159,22 @@
@Test
fun testNotAvailableControls() {
- controlsComponent = ControlsComponent(
- false,
- mContext,
- { controlsController },
- { controlsUiController },
- { controlsListingController },
- lockPatternUtils,
- keyguardStateController,
- userTracker,
- secureSettings
- )
+ featureEnabled = false
tile = createTile()
assertThat(tile.isAvailable).isFalse()
}
@Test
- fun testNotAvailableFlag() {
+ fun testAvailableControlsSettingOff() {
+ `when`(controlsController.available).thenReturn(false)
+
+ tile = createTile()
+ assertThat(tile.isAvailable).isTrue()
+ }
+
+ @Test
+ fun testNotAvailableControlsLockscreenFlag() {
globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 0)
tile = createTile()
@@ -207,11 +213,26 @@
}
@Test
+ fun testStateUnavailableIfNotEnabled() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+ `when`(controlsComponent.isEnabled()).thenReturn(false)
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
fun testStateAvailableIfListings() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
capture(listingCallbackCaptor)
)
+ `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
testableLooper.processAllMessages()
@@ -220,6 +241,21 @@
}
@Test
+ fun testStateInactiveIfLocked() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+ `when`(controlsComponent.getVisibility())
+ .thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK)
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
fun testMoveBetweenStates() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
@@ -249,6 +285,7 @@
any(LifecycleOwner::class.java),
capture(listingCallbackCaptor)
)
+ `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
testableLooper.processAllMessages()
@@ -260,6 +297,24 @@
}
@Test
+ fun testNoDialogWhenInactive() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+ `when`(controlsComponent.getVisibility())
+ .thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK)
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ tile.click()
+ testableLooper.processAllMessages()
+
+ verify(controlsDialog, never()).show(any(ControlsUiController::class.java))
+ }
+
+ @Test
fun testDialogDismissedOnDestroy() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index 05cf33a..b493b9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -171,62 +171,10 @@
}
@Test
- public void testSuppressSystemAlertNotification() {
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
- StatusBarNotification sbn = mRow.getEntry().getSbn();
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
- sbn.getNotification().extras = bundle;
-
- assertTrue(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testDoNotSuppressSystemAlertNotification() {
- StatusBarNotification sbn = mRow.getEntry().getSbn();
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
- sbn.getNotification().extras = bundle;
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
-
- assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testDoNotSuppressMalformedSystemAlertNotification() {
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-
- // missing extra
- assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
-
- StatusBarNotification sbn = mRow.getEntry().getSbn();
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{});
- sbn.getNotification().extras = bundle;
-
- // extra missing values
- assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
public void testShouldFilterHiddenNotifications() {
initStatusBarNotification(false);
// setup
when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
// test should filter out hidden notifications:
// hidden
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index 0954621..2784568 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -115,41 +115,6 @@
}
@Test
- public void filterTest_systemAlertNotificationUnnecessary() {
- // GIVEN the alert notification isn't needed for this user
- final Bundle extras = new Bundle();
- extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS,
- new String[]{TEST_PKG});
- mEntryBuilder.modifyNotification(mContext)
- .setExtras(extras);
- NotificationEntry entry = mEntryBuilder.build();
- StatusBarNotification sbn = entry.getSbn();
- when(mForegroundServiceController.isSystemAlertWarningNeeded(sbn.getUserId(), TEST_PKG))
- .thenReturn(false);
-
- // GIVEN the notification is a system alert notification + not a disclosure notification
- when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(true);
- when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false);
-
-
- // THEN filter out the notification
- assertTrue(mForegroundFilter.shouldFilterOut(entry, 0));
- }
-
- @Test
- public void filterTest_doNotFilter() {
- NotificationEntry entry = mEntryBuilder.build();
- StatusBarNotification sbn = entry.getSbn();
-
- // GIVEN the notification isn't a system alert notification nor a disclosure notification
- when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(false);
- when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false);
-
- // THEN don't filter out the notification
- assertFalse(mForegroundFilter.shouldFilterOut(entry, 0));
- }
-
- @Test
public void testIncludeFGSInSection_importanceDefault() {
// GIVEN the notification represents a colorized foreground service with > min importance
mEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 461f64e..84fb368 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -21,6 +21,8 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -49,6 +51,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -101,6 +104,7 @@
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
+ @Mock private FeatureFlags mFeatureFlags;
@Before
@UiThreadTest
@@ -139,8 +143,8 @@
mGroupMembershipManger,
mGroupExpansionManager,
mStatusBarStateController,
- mAmbientState
- );
+ mAmbientState,
+ mFeatureFlags);
mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
mNotificationSwipeHelper);
mStackScroller = spy(mStackScrollerInternal);
@@ -205,8 +209,8 @@
@Test
@UiThreadTest
public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
- final float expectedHeight[] = {0f};
- final float expectedAppear[] = {0f};
+ final float[] expectedHeight = {0f};
+ final float[] expectedAppear = {0f};
mStackScroller.addOnExpandedHeightChangedListener((height, appear) -> {
Assert.assertEquals(expectedHeight[0], height, 0);
@@ -222,6 +226,29 @@
}
@Test
+ @UiThreadTest
+ public void testSetExpandedHeight_withSplitShade_doesntInterpolateStackHeight() {
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ final int[] expectedStackHeight = {0};
+
+ mStackScroller.addOnExpandedHeightChangedListener((expandedHeight, appear) -> {
+ assertWithMessage("Given shade enabled: %s",
+ mFeatureFlags.isTwoColumnNotificationShadeEnabled())
+ .that(mStackScroller.getHeight())
+ .isEqualTo(expectedStackHeight[0]);
+ });
+
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+ expectedStackHeight[0] = 0;
+ mStackScroller.setExpandedHeight(100f);
+
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ expectedStackHeight[0] = 100;
+ mStackScroller.setExpandedHeight(100f);
+ }
+
+
+ @Test
public void manageNotifications_visible() {
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 8dea84c..6e0cbd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -197,9 +198,9 @@
mHeadsUpAppearanceController.destroy();
verify(mHeadsUpManager).removeListener(any());
verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
- verify(mPanelView).removeVerticalTranslationListener(any());
+ verify(mPanelView).setVerticalTranslationListener(isNull());
verify(mPanelView).removeTrackingHeadsUpListener(any());
- verify(mPanelView).setHeadsUpAppearanceController(any());
+ verify(mPanelView).setHeadsUpAppearanceController(isNull());
verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any());
verify(mStackScrollerController).removeOnLayoutChangeListener(any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index e788a1c..dd31f52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -18,19 +18,22 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.IdRes;
import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.res.Configuration;
@@ -45,6 +48,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -53,6 +57,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardClockSwitch;
@@ -100,7 +105,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
@@ -116,8 +120,6 @@
@Mock
private StatusBar mStatusBar;
@Mock
- private SysuiStatusBarStateController mStatusBarStateController;
- @Mock
private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock
private KeyguardBottomAreaView mKeyguardBottomArea;
@@ -227,7 +229,10 @@
private AmbientState mAmbientState;
@Mock
private UserManager mUserManager;
+ @Mock
+ private UiEventLogger mUiEventLogger;
+ private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
@@ -235,6 +240,8 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger);
+
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
when(mHeadsUpCallback.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
@@ -258,12 +265,14 @@
when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class));
when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
+ when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class));
when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer);
when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
when(mView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
- when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
+ mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
+ mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
when(mView.findViewById(R.id.notification_container_parent))
.thenReturn(mNotificationContainerParent);
FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
@@ -293,10 +302,6 @@
.thenReturn(mKeyguardClockSwitchController);
when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
.thenReturn(mKeyguardStatusViewController);
- when(mQsFrame.getLayoutParams()).thenReturn(
- new ViewGroup.LayoutParams(600, 400));
- when(mNotificationStackScrollLayoutController.getLayoutParams()).thenReturn(
- new ViewGroup.LayoutParams(600, 400));
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
@@ -336,17 +341,19 @@
ArgumentCaptor.forClass(View.AccessibilityDelegate.class);
verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture());
mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue();
+ mNotificationPanelViewController.mStatusBarStateController
+ .addCallback(mNotificationPanelViewController.mStatusBarStateListener);
+ mNotificationPanelViewController
+ .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
}
@Test
public void testSetDozing_notifiesNsslAndStateController() {
- mNotificationPanelViewController.setDozing(true /* dozing */, true /* animate */,
+ mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */,
null /* touch */);
- InOrder inOrder = inOrder(
- mNotificationStackScrollLayoutController, mStatusBarStateController);
- inOrder.verify(mNotificationStackScrollLayoutController)
- .setDozing(eq(true), eq(true), eq(null));
- inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true));
+ verify(mNotificationStackScrollLayoutController)
+ .setDozing(eq(true), eq(false), eq(null));
+ assertThat(mStatusBarStateController.getDozeAmount()).isEqualTo(1f);
}
@Test
@@ -442,9 +449,8 @@
@Test
public void testAllChildrenOfNotificationContainer_haveIds() {
- when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
-
+ enableSplitShade();
+ mNotificationContainerParent.removeAllViews();
mNotificationContainerParent.addView(newViewWithId(1));
mNotificationContainerParent.addView(newViewWithId(View.NO_ID));
@@ -457,36 +463,92 @@
@Test
public void testSinglePaneShadeLayout_isAlignedToParent() {
when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
- mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
- mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
mNotificationPanelViewController.updateResources();
- ConstraintSet constraintSet = new ConstraintSet();
- constraintSet.clone(mNotificationContainerParent);
- ConstraintSet.Layout qsFrameLayout = constraintSet.getConstraint(R.id.qs_frame).layout;
- ConstraintSet.Layout stackScrollerLayout = constraintSet.getConstraint(
- R.id.notification_stack_scroller).layout;
- assertThat(qsFrameLayout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID);
- assertThat(stackScrollerLayout.startToStart).isEqualTo(ConstraintSet.PARENT_ID);
+ assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd)
+ .isEqualTo(ConstraintSet.PARENT_ID);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart)
+ .isEqualTo(ConstraintSet.PARENT_ID);
}
@Test
public void testSplitShadeLayout_isAlignedToGuideline() {
- when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
- mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
- mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
+ enableSplitShade();
mNotificationPanelViewController.updateResources();
- ConstraintSet constraintSet = new ConstraintSet();
- constraintSet.clone(mNotificationContainerParent);
- ConstraintSet.Layout qsFrameLayout = constraintSet.getConstraint(R.id.qs_frame).layout;
- ConstraintSet.Layout stackScrollerLayout = constraintSet.getConstraint(
- R.id.notification_stack_scroller).layout;
- assertThat(qsFrameLayout.endToEnd).isEqualTo(R.id.qs_edge_guideline);
- assertThat(stackScrollerLayout.startToStart).isEqualTo(R.id.qs_edge_guideline);
+ assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd)
+ .isEqualTo(R.id.qs_edge_guideline);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart)
+ .isEqualTo(R.id.qs_edge_guideline);
+ }
+
+ @Test
+ public void testSinglePaneShadeLayout_childrenHaveConstantWidth() {
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+
+ mNotificationPanelViewController.updateResources();
+
+ assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth)
+ .isEqualTo(mResources.getDimensionPixelSize(R.dimen.qs_panel_width));
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth)
+ .isEqualTo(mResources.getDimensionPixelSize(R.dimen.notification_panel_width));
+ }
+
+ @Test
+ public void testSplitShadeLayout_childrenHaveZeroWidth() {
+ enableSplitShade();
+
+ mNotificationPanelViewController.updateResources();
+
+ assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth).isEqualTo(0);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth).isEqualTo(0);
+ }
+
+ @Test
+ public void testOnDragDownEvent_horizontalTranslationIsZeroForSplitShade() {
+ when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(350f);
+ when(mView.getWidth()).thenReturn(800);
+ enableSplitShade();
+
+ onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN,
+ 200f /* x position */, 0f, 0));
+
+ verify(mQsFrame).setTranslationX(0);
+ }
+
+ @Test
+ public void testCanCollapsePanelOnTouch_trueForKeyGuard() {
+ mStatusBarStateController.setState(KEYGUARD);
+
+ assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
+ }
+
+ @Test
+ public void testCanCollapsePanelOnTouch_trueWhenScrolledToBottom() {
+ mStatusBarStateController.setState(SHADE);
+ when(mNotificationStackScrollLayoutController.isScrolledToBottom()).thenReturn(true);
+
+ assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
+ }
+
+ @Test
+ public void testCanCollapsePanelOnTouch_trueWhenInSettings() {
+ mStatusBarStateController.setState(SHADE);
+ mNotificationPanelViewController.setQsExpanded(true);
+
+ assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
+ }
+
+ @Test
+ public void testCanCollapsePanelOnTouch_falseInDualPaneShade() {
+ mStatusBarStateController.setState(SHADE);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ mNotificationPanelViewController.setQsExpanded(true);
+
+ assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse();
}
private View newViewWithId(int id) {
@@ -499,6 +561,17 @@
return view;
}
+ private ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) {
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mNotificationContainerParent);
+ return constraintSet.getConstraint(id).layout;
+ }
+
+ private void enableSplitShade() {
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ }
+
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index 45828c3..e798207 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -23,6 +23,7 @@
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SETTINGS;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SYSUI;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_THEME_PICKER;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SHAPE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
import static com.android.systemui.theme.ThemeOverlayApplier.SETTINGS_PACKAGE;
@@ -114,6 +115,8 @@
ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, false),
createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE,
ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, false),
+ createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE,
+ ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, false),
createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_FONT,
ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, false),
createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SHAPE,
@@ -124,6 +127,8 @@
ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, true),
createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE,
ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, true),
+ createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE,
+ ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, true),
createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_FONT,
ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, true),
createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SHAPE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
index c5a197e..242fe9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.util.sensors;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -86,6 +88,29 @@
}
@Test
+ public void testNotLoaded() {
+ mFakeProximitySensor.setSensorAvailable(false);
+
+ assertThat(mTestableCallback.mLastResult).isNull();
+ assertThat(mTestableCallback.mNumCalls).isEqualTo(0);
+
+ mProximityCheck.check(100, mTestableCallback);
+
+ assertThat(mTestableCallback.mLastResult).isNull();
+ assertThat(mTestableCallback.mNumCalls).isEqualTo(1);
+
+ mFakeProximitySensor.setSensorAvailable(true);
+
+ mProximityCheck.check(100, mTestableCallback);
+
+ mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0));
+ mFakeProximitySensor.alertListeners();
+
+ assertThat(mTestableCallback.mLastResult).isNotNull();
+ assertThat(mTestableCallback.mNumCalls).isEqualTo(2);
+ }
+
+ @Test
public void testProxDoesntCancelOthers() {
assertFalse(mFakeProximitySensor.isRegistered());
// We don't need our "other" listener to do anything. Just ensure our sensor is registered.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
index 97d4aa7..7d8a288 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
@@ -14,7 +14,6 @@
package com.android.systemui.utils.leaks;
-import android.os.UserHandle;
import android.testing.LeakCheck;
import com.android.systemui.tuner.TunerService;
@@ -78,12 +77,15 @@
}
@Override
- public void setTunerEnabled(UserHandle user, boolean enabled) {
+ public void setTunerEnabled(boolean enabled) {
mEnabled = enabled;
}
@Override
- public boolean isTunerEnabled(UserHandle user) {
+ public boolean isTunerEnabled() {
return mEnabled;
}
+
+ @Override
+ public void showResetRequest(Runnable onDisabled) {}
}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 6dcad25..3502baa 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -23,7 +23,7 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.ConnectivityManager;
+import android.net.VpnManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
@@ -42,7 +42,7 @@
private static final String TAG = "VpnDisconnected";
- private ConnectivityManager mService;
+ private VpnManager mService;
private int mUserId;
private String mVpnPackage;
@@ -51,8 +51,8 @@
super.onCreate(savedInstanceState);
mUserId = UserHandle.myUserId();
- final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
- mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId);
+ final VpnManager vm = getSystemService(VpnManager.class);
+ mVpnPackage = vm.getAlwaysOnVpnPackageForUser(mUserId);
if (mVpnPackage == null) {
finish();
return;
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index aab01d0..fb23678 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -21,7 +21,6 @@
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.net.ConnectivityManager;
import android.net.VpnManager;
import android.os.Bundle;
import android.os.UserHandle;
@@ -45,7 +44,6 @@
private String mPackage;
- private ConnectivityManager mCm; // TODO: switch entirely to VpnManager once VPN code moves
private VpnManager mVm;
public ConfirmDialog() {
@@ -60,7 +58,6 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPackage = getCallingPackage();
- mCm = getSystemService(ConnectivityManager.class);
mVm = getSystemService(VpnManager.class);
if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) {
@@ -72,7 +69,7 @@
finish();
return;
}
- final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
+ final String alwaysOnVpnPackage = mVm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
// Can't prepare new vpn app when another vpn is always-on
if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
finish();
diff --git a/packages/overlays/AccentColorAmethystOverlay/Android.bp b/packages/overlays/AccentColorAmethystOverlay/Android.bp
index 7519b12..186d770 100644
--- a/packages/overlays/AccentColorAmethystOverlay/Android.bp
+++ b/packages/overlays/AccentColorAmethystOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorAmethystOverlay",
theme: "AccentColorAmethyst",
diff --git a/packages/overlays/AccentColorAquamarineOverlay/Android.bp b/packages/overlays/AccentColorAquamarineOverlay/Android.bp
index 4469b36..7fd64f3 100644
--- a/packages/overlays/AccentColorAquamarineOverlay/Android.bp
+++ b/packages/overlays/AccentColorAquamarineOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorAquamarineOverlay",
theme: "AccentColorAquamarine",
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.bp b/packages/overlays/AccentColorBlackOverlay/Android.bp
index bfee26e..ac923eb 100644
--- a/packages/overlays/AccentColorBlackOverlay/Android.bp
+++ b/packages/overlays/AccentColorBlackOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorBlackOverlay",
theme: "AccentColorBlack",
diff --git a/packages/overlays/AccentColorCarbonOverlay/Android.bp b/packages/overlays/AccentColorCarbonOverlay/Android.bp
index 47f66dd..f4f1b8b 100644
--- a/packages/overlays/AccentColorCarbonOverlay/Android.bp
+++ b/packages/overlays/AccentColorCarbonOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorCarbonOverlay",
theme: "AccentColorCarbon",
diff --git a/packages/overlays/AccentColorCinnamonOverlay/Android.bp b/packages/overlays/AccentColorCinnamonOverlay/Android.bp
index 8250315..53899bf 100644
--- a/packages/overlays/AccentColorCinnamonOverlay/Android.bp
+++ b/packages/overlays/AccentColorCinnamonOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorCinnamonOverlay",
theme: "AccentColorCinnamon",
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.bp b/packages/overlays/AccentColorGreenOverlay/Android.bp
index 15b50c7..5b1f744 100644
--- a/packages/overlays/AccentColorGreenOverlay/Android.bp
+++ b/packages/overlays/AccentColorGreenOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorGreenOverlay",
theme: "AccentColorGreen",
diff --git a/packages/overlays/AccentColorOceanOverlay/Android.bp b/packages/overlays/AccentColorOceanOverlay/Android.bp
index 6ad63bc..a8588304 100644
--- a/packages/overlays/AccentColorOceanOverlay/Android.bp
+++ b/packages/overlays/AccentColorOceanOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorOceanOverlay",
theme: "AccentColorOcean",
diff --git a/packages/overlays/AccentColorOrchidOverlay/Android.bp b/packages/overlays/AccentColorOrchidOverlay/Android.bp
index b669333..31ed309 100644
--- a/packages/overlays/AccentColorOrchidOverlay/Android.bp
+++ b/packages/overlays/AccentColorOrchidOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorOrchidOverlay",
theme: "AccentColorOrchid",
diff --git a/packages/overlays/AccentColorPaletteOverlay/Android.bp b/packages/overlays/AccentColorPaletteOverlay/Android.bp
index eeefd16..a6cc1de 100644
--- a/packages/overlays/AccentColorPaletteOverlay/Android.bp
+++ b/packages/overlays/AccentColorPaletteOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorPaletteOverlay",
theme: "AccentColorPalette",
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.bp b/packages/overlays/AccentColorPurpleOverlay/Android.bp
index ead95df..80e0ab1 100644
--- a/packages/overlays/AccentColorPurpleOverlay/Android.bp
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorPurpleOverlay",
theme: "AccentColorPurple",
diff --git a/packages/overlays/AccentColorSandOverlay/Android.bp b/packages/overlays/AccentColorSandOverlay/Android.bp
index f70578a..771abca 100644
--- a/packages/overlays/AccentColorSandOverlay/Android.bp
+++ b/packages/overlays/AccentColorSandOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorSandOverlay",
theme: "AccentColorSand",
diff --git a/packages/overlays/AccentColorSpaceOverlay/Android.bp b/packages/overlays/AccentColorSpaceOverlay/Android.bp
index 1d713df..8e4abac 100644
--- a/packages/overlays/AccentColorSpaceOverlay/Android.bp
+++ b/packages/overlays/AccentColorSpaceOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorSpaceOverlay",
theme: "AccentColorSpace",
diff --git a/packages/overlays/AccentColorTangerineOverlay/Android.bp b/packages/overlays/AccentColorTangerineOverlay/Android.bp
index d3b1e54..75c708e 100644
--- a/packages/overlays/AccentColorTangerineOverlay/Android.bp
+++ b/packages/overlays/AccentColorTangerineOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "AccentColorTangerineOverlay",
theme: "AccentColorTangerine",
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp
index b8def987..8e03809 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "DisplayCutoutEmulationCornerOverlay",
theme: "DisplayCutoutEmulationCorner",
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp
index b64ddfd..afa5b64 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "DisplayCutoutEmulationDoubleOverlay",
theme: "DisplayCutoutEmulationDouble",
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp
index 86cfebf..eae907d 100644
--- a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "DisplayCutoutEmulationHoleOverlay",
theme: "DisplayCutoutEmulationHole",
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp
index 28ad9db..25bc676 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "DisplayCutoutEmulationNarrowOverlay",
theme: "DisplayCutoutEmulationNarrow",
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp
index cd00386..2828612 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "DisplayCutoutEmulationTallOverlay",
theme: "DisplayCutoutEmulationTall",
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp
index d5fe683..66be777 100644
--- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "DisplayCutoutEmulationWaterfallOverlay",
theme: "DisplayCutoutEmulationWaterfall",
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp
index 0157ec4..e71cefe 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "DisplayCutoutEmulationWideOverlay",
theme: "DisplayCutoutEmulationWide",
diff --git a/packages/overlays/FontNotoSerifSourceOverlay/Android.bp b/packages/overlays/FontNotoSerifSourceOverlay/Android.bp
index 7fd145b..231295b 100644
--- a/packages/overlays/FontNotoSerifSourceOverlay/Android.bp
+++ b/packages/overlays/FontNotoSerifSourceOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "FontNotoSerifSourceOverlay",
theme: "FontNotoSerifSource",
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/Android.bp b/packages/overlays/IconPackCircularAndroidOverlay/Android.bp
index cd5829a..7040358 100644
--- a/packages/overlays/IconPackCircularAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackCircularAndroidOverlay",
theme: "IconPackCircularAndroid",
diff --git a/packages/overlays/IconPackCircularLauncherOverlay/Android.bp b/packages/overlays/IconPackCircularLauncherOverlay/Android.bp
index 5f2491d..4f8b663 100644
--- a/packages/overlays/IconPackCircularLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackCircularLauncherOverlay",
theme: "IconPackCircularLauncher",
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/Android.bp b/packages/overlays/IconPackCircularSettingsOverlay/Android.bp
index d7bc657..93220c8 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackCircularSettingsOverlay",
theme: "IconPackCircularSettings",
diff --git a/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp b/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp
index 73b8cd8..4eaa420 100644
--- a/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackCircularSystemUIOverlay",
theme: "IconPackCircularSystemUI",
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp b/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp
index 5063932..5105b79 100644
--- a/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackCircularThemePickerOverlay",
theme: "IconPackCircularThemePicker",
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/Android.bp b/packages/overlays/IconPackFilledAndroidOverlay/Android.bp
index 83f3656..3c4025d 100644
--- a/packages/overlays/IconPackFilledAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackFilledAndroidOverlay",
theme: "IconPackFilledAndroid",
diff --git a/packages/overlays/IconPackFilledLauncherOverlay/Android.bp b/packages/overlays/IconPackFilledLauncherOverlay/Android.bp
index 6ca2566..3c5078c 100644
--- a/packages/overlays/IconPackFilledLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackFilledLauncherOverlay",
theme: "IconPackFilledLauncher",
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/Android.bp b/packages/overlays/IconPackFilledSettingsOverlay/Android.bp
index 8551bd5..b5148c2 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackFilledSettingsOverlay",
theme: "IconPackFilledSettings",
diff --git a/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp b/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp
index 684deb4..eb040a5 100644
--- a/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackFilledSystemUIOverlay",
theme: "IconPackFilledSystemUI",
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp b/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp
index dae378f..bee4808 100644
--- a/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackFilledThemePickerOverlay",
theme: "IconPackFilledThemePicker",
diff --git a/packages/overlays/IconPackKaiAndroidOverlay/Android.bp b/packages/overlays/IconPackKaiAndroidOverlay/Android.bp
index 4161e25..ee588c1 100644
--- a/packages/overlays/IconPackKaiAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackKaiAndroidOverlay",
theme: "IconPackKaiAndroid",
diff --git a/packages/overlays/IconPackKaiLauncherOverlay/Android.bp b/packages/overlays/IconPackKaiLauncherOverlay/Android.bp
index 4bf8f11..dcdad7a 100644
--- a/packages/overlays/IconPackKaiLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackKaiLauncherOverlay",
theme: "IconPackKaiLauncher",
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/Android.bp b/packages/overlays/IconPackKaiSettingsOverlay/Android.bp
index c43f9e5..974bb54 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackKaiSettingsOverlay",
theme: "IconPackKaiSettings",
diff --git a/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp b/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp
index 22a8c28..b04ca61 100644
--- a/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackKaiSystemUIOverlay",
theme: "IconPackKaiSystemUI",
diff --git a/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp b/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp
index 85eb1c4..875cd1d 100644
--- a/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackKaiThemePickerOverlay",
theme: "IconPackKaiThemePicker",
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp b/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp
index 948f015..cb7b013 100644
--- a/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackRoundedAndroidOverlay",
theme: "IconPackRoundedAndroid",
diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp b/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp
index 5fbe635..8ab6d95 100644
--- a/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackRoundedLauncherOverlay",
theme: "IconPackRoundedLauncher",
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp b/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp
index b1a0713..ee2f98a9 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackRoundedSettingsOverlay",
theme: "IconPackRoundedSettings",
diff --git a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp
index 6432998..ee0220a 100644
--- a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackRoundedSystemUIOverlay",
theme: "IconPackRoundedSystemUI",
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp
index 95e1d3a..d74765c 100644
--- a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackRoundedThemePickerOverlay",
theme: "IconPackRoundedTheme",
diff --git a/packages/overlays/IconPackSamAndroidOverlay/Android.bp b/packages/overlays/IconPackSamAndroidOverlay/Android.bp
index e8c33b1..2e9dc34 100644
--- a/packages/overlays/IconPackSamAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackSamAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackSamAndroidOverlay",
theme: "IconPackSamAndroid",
diff --git a/packages/overlays/IconPackSamLauncherOverlay/Android.bp b/packages/overlays/IconPackSamLauncherOverlay/Android.bp
index a469646..aa0cf00 100644
--- a/packages/overlays/IconPackSamLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackSamLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackSamLauncherOverlay",
theme: "IconPackSamLauncher",
diff --git a/packages/overlays/IconPackSamSettingsOverlay/Android.bp b/packages/overlays/IconPackSamSettingsOverlay/Android.bp
index bc1fa45..a62037f 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackSamSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackSamSettingsOverlay",
theme: "IconPackSamSettings",
diff --git a/packages/overlays/IconPackSamSystemUIOverlay/Android.bp b/packages/overlays/IconPackSamSystemUIOverlay/Android.bp
index db77352..96ba7a0 100644
--- a/packages/overlays/IconPackSamSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackSamSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackSamSystemUIOverlay",
theme: "IconPackSamSystemUI",
diff --git a/packages/overlays/IconPackSamThemePickerOverlay/Android.bp b/packages/overlays/IconPackSamThemePickerOverlay/Android.bp
index c216868..7376f03 100644
--- a/packages/overlays/IconPackSamThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackSamThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackSamThemePickerOverlay",
theme: "IconPackSamThemePicker",
diff --git a/packages/overlays/IconPackVictorAndroidOverlay/Android.bp b/packages/overlays/IconPackVictorAndroidOverlay/Android.bp
index de62af1..ee73778 100644
--- a/packages/overlays/IconPackVictorAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackVictorAndroidOverlay",
theme: "IconPackVictorAndroid",
diff --git a/packages/overlays/IconPackVictorLauncherOverlay/Android.bp b/packages/overlays/IconPackVictorLauncherOverlay/Android.bp
index fc4c360..a0cd45a 100644
--- a/packages/overlays/IconPackVictorLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackVictorLauncherOverlay",
theme: "IconPackVictorLauncher",
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/Android.bp b/packages/overlays/IconPackVictorSettingsOverlay/Android.bp
index 046bb3d..7807c6b 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackVictorSettingsOverlay",
theme: "IconPackVictorSettings",
diff --git a/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp b/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp
index b8a9e77..2deb6cd 100644
--- a/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackVictorSystemUIOverlay",
theme: "IconPackVictorSystemUI",
diff --git a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp
index 6f0144e..a18ebb3 100644
--- a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconPackVictorThemePickerOverlay",
theme: "IconPackVictorThemePicker",
diff --git a/packages/overlays/IconShapeHeartOverlay/Android.bp b/packages/overlays/IconShapeHeartOverlay/Android.bp
index ec55712..1da8f4f 100644
--- a/packages/overlays/IconShapeHeartOverlay/Android.bp
+++ b/packages/overlays/IconShapeHeartOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconShapeHeartOverlay",
theme: "IconShapeHeart",
diff --git a/packages/overlays/IconShapePebbleOverlay/Android.bp b/packages/overlays/IconShapePebbleOverlay/Android.bp
index 7dc4fde..fa2a5bb 100644
--- a/packages/overlays/IconShapePebbleOverlay/Android.bp
+++ b/packages/overlays/IconShapePebbleOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconShapePebbleOverlay",
theme: "IconShapePebble",
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.bp b/packages/overlays/IconShapeRoundedRectOverlay/Android.bp
index b8b8531..5052d08f 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/Android.bp
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconShapeRoundedRectOverlay",
theme: "IconShapeRoundedRect",
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.bp b/packages/overlays/IconShapeSquareOverlay/Android.bp
index fdeffee..1176abd 100644
--- a/packages/overlays/IconShapeSquareOverlay/Android.bp
+++ b/packages/overlays/IconShapeSquareOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconShapeSquareOverlay",
theme: "IconShapeSquare",
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.bp b/packages/overlays/IconShapeSquircleOverlay/Android.bp
index 468f0f7..8c219f3 100644
--- a/packages/overlays/IconShapeSquircleOverlay/Android.bp
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconShapeSquircleOverlay",
theme: "IconShapeSquircle",
diff --git a/packages/overlays/IconShapeTaperedRectOverlay/Android.bp b/packages/overlays/IconShapeTaperedRectOverlay/Android.bp
index 1e48cd1..78855e8 100644
--- a/packages/overlays/IconShapeTaperedRectOverlay/Android.bp
+++ b/packages/overlays/IconShapeTaperedRectOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconShapeTaperedRectOverlay",
theme: "IconShapeTaperedRect",
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.bp b/packages/overlays/IconShapeTeardropOverlay/Android.bp
index 017d58ec..dd36f4f 100644
--- a/packages/overlays/IconShapeTeardropOverlay/Android.bp
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconShapeTeardropOverlay",
theme: "IconShapeTeardrop",
diff --git a/packages/overlays/IconShapeVesselOverlay/Android.bp b/packages/overlays/IconShapeVesselOverlay/Android.bp
index ba3b309..2e7f8bc 100644
--- a/packages/overlays/IconShapeVesselOverlay/Android.bp
+++ b/packages/overlays/IconShapeVesselOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "IconShapeVesselOverlay",
theme: "IconShapeVessel",
diff --git a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp
index e4fcce1..35f671ba 100644
--- a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp
+++ b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "NavigationBarMode2ButtonOverlay",
theme: "NavigationBarMode2Button",
diff --git a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp
index 9b6c998..fe9cc81 100644
--- a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp
+++ b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "NavigationBarMode3ButtonOverlay",
theme: "NavigationBarMode3Button",
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp
index ba29045..791f420 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "NavigationBarModeGesturalOverlay",
theme: "NavigationBarModeGestural",
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp
index 0c688a9..28f9f33 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp
+++ b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "NavigationBarModeGesturalOverlayExtraWideBack",
theme: "NavigationBarModeGesturalExtraWideBack",
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp
index 85d514f..f8a5603 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp
+++ b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "NavigationBarModeGesturalOverlayNarrowBack",
theme: "NavigationBarModeGesturalNarrowBack",
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp
index 84be626..60ee6d5 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp
+++ b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "NavigationBarModeGesturalOverlayWideBack",
theme: "NavigationBarModeGesturalWideBack",
diff --git a/packages/overlays/OneHandedModeGesturalOverlay/Android.bp b/packages/overlays/OneHandedModeGesturalOverlay/Android.bp
index 9c9d0ef..468069d 100644
--- a/packages/overlays/OneHandedModeGesturalOverlay/Android.bp
+++ b/packages/overlays/OneHandedModeGesturalOverlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
runtime_resource_overlay {
name: "OneHandedModeGesturalOverlay",
theme: "OneHandedModeGestural",
diff --git a/packages/services/CameraExtensionsProxy/Android.bp b/packages/services/CameraExtensionsProxy/Android.bp
index e2e4af2..ea0703e 100644
--- a/packages/services/CameraExtensionsProxy/Android.bp
+++ b/packages/services/CameraExtensionsProxy/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "CameraExtensionsProxy",
srcs: ["src/**/*.java"],
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ea1473e..c63c2e1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -777,6 +777,10 @@
// performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
+
+ if (Binder.getCallingPid() == OWN_PROCESS_ID) {
+ return new ArrayList<>(getUserStateLocked(resolvedUserId).mInstalledServices);
+ }
return getUserStateLocked(resolvedUserId).mInstalledServices;
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 809304b..7518c7a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2673,7 +2673,7 @@
AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
info.widgetFeatures = sa.getInt(
com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures, 0);
- info.descriptionResource = sa.getResourceId(
+ info.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AppWidgetProviderInfo_description,
Resources.ID_NULL);
sa.recycle();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 76c8d30..01055be 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1422,7 +1422,9 @@
}
@Override
- public void onDeviceDisconnected(BluetoothDevice device) {
+ public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {
+ Slog.d(LOG_TAG, device.getAddress() + " disconnected w/ reason: (" + reason + ") "
+ + BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonText(reason));
CompanionDeviceManagerService.this.onDeviceDisconnected(device.getAddress());
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9d86f4e..f4138d1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -188,14 +188,16 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.BitUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.PermissionUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.DataConnectionStats;
@@ -1510,7 +1512,7 @@
@Override
public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) {
- NetworkStack.checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
return getActiveNetworkForUidInternal(uid, ignoreBlocked);
}
@@ -1533,7 +1535,7 @@
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
- NetworkStack.checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
final NetworkState state = getUnfilteredActiveNetworkState(uid);
filterNetworkStateForUid(state, uid, ignoreBlocked);
return state.networkInfo;
@@ -1877,7 +1879,7 @@
@Override
public NetworkState[] getAllNetworkState() {
// This contains IMSI details, so make sure the caller is privileged.
- NetworkStack.checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
final ArrayList<NetworkState> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
@@ -2301,7 +2303,7 @@
// Public because it's used by mLockdownTracker.
public void sendConnectedBroadcast(NetworkInfo info) {
- NetworkStack.checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
}
@@ -2565,13 +2567,13 @@
if (!checkDumpPermission(mContext, TAG, pw)) return;
if (asProto) return;
- if (ArrayUtils.contains(args, DIAG_ARG)) {
+ if (CollectionUtils.contains(args, DIAG_ARG)) {
dumpNetworkDiagnostics(pw);
return;
- } else if (ArrayUtils.contains(args, NETWORK_ARG)) {
+ } else if (CollectionUtils.contains(args, NETWORK_ARG)) {
dumpNetworks(pw);
return;
- } else if (ArrayUtils.contains(args, REQUEST_ARG)) {
+ } else if (CollectionUtils.contains(args, REQUEST_ARG)) {
dumpNetworkRequests(pw);
return;
}
@@ -2642,7 +2644,7 @@
pw.println();
- if (ArrayUtils.contains(args, SHORT_ARG) == false) {
+ if (!CollectionUtils.contains(args, SHORT_ARG)) {
pw.println();
pw.println("mNetworkRequestInfoLogs (most recent first):");
pw.increaseIndent();
@@ -4684,7 +4686,7 @@
@Override
public void setGlobalProxy(final ProxyInfo proxyProperties) {
- NetworkStack.checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
mProxyTracker.setGlobalProxy(proxyProperties);
}
@@ -4809,7 +4811,7 @@
}
}
- if (ArrayUtils.isEmpty(underlyingNetworks)) return null;
+ if (CollectionUtils.isEmpty(underlyingNetworks)) return null;
List<String> interfaces = new ArrayList<>();
for (Network network : underlyingNetworks) {
@@ -4853,7 +4855,7 @@
if (!nai.supportsUnderlyingNetworks()) return false;
final Network[] underlying = underlyingNetworksOrDefault(
nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks);
- return ArrayUtils.contains(underlying, network);
+ return CollectionUtils.contains(underlying, network);
}
/**
@@ -4886,7 +4888,7 @@
@Override
public void setRequireVpnForUids(boolean requireVpn, UidRange[] ranges) {
- NetworkStack.checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_REQUIRE_VPN_FOR_UIDS,
encodeBool(requireVpn), 0 /* arg2 */, ranges));
}
@@ -5317,8 +5319,7 @@
}
}
}
- // TODO: use NetworkStackUtils.convertToIntArray after moving it
- return ArrayUtils.convertToIntArray(new ArrayList<>(thresholds));
+ return CollectionUtils.toIntArray(new ArrayList<>(thresholds));
}
private void updateSignalStrengthThresholds(
@@ -6437,7 +6438,7 @@
@NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
underlyingNetworks = underlyingNetworksOrDefault(
agentCaps.getOwnerUid(), underlyingNetworks);
- int[] transportTypes = agentCaps.getTransportTypes();
+ long transportTypes = BitUtils.packBits(agentCaps.getTransportTypes());
int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
// metered if any underlying is metered, or originally declared metered by the agent.
@@ -6456,7 +6457,7 @@
final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
hadUnderlyingNetworks = true;
for (int underlyingType : underlyingCaps.getTransportTypes()) {
- transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
+ transportTypes |= 1L << underlyingType;
}
// Merge capabilities of this underlying network. For bandwidth, assume the
@@ -6487,7 +6488,7 @@
suspended = false;
}
- newNc.setTransportTypes(transportTypes);
+ newNc.setTransportTypes(BitUtils.unpackBits(transportTypes));
newNc.setLinkDownstreamBandwidthKbps(downKbps);
newNc.setLinkUpstreamBandwidthKbps(upKbps);
newNc.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
@@ -8552,14 +8553,14 @@
for (NetworkAgentInfo virtual : mNetworkAgentInfos) {
if (virtual.supportsUnderlyingNetworks()
&& virtual.networkCapabilities.getOwnerUid() == callbackUid
- && ArrayUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) {
+ && CollectionUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) {
return true;
}
}
// Administrator UIDs also contains the Owner UID
final int[] administratorUids = nai.networkCapabilities.getAdministratorUids();
- return ArrayUtils.contains(administratorUids, callbackUid);
+ return CollectionUtils.contains(administratorUids, callbackUid);
}
@Override
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 88ce220..e29e894 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -28,6 +28,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.IDynamicSystemService;
+import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.Slog;
@@ -40,6 +41,7 @@
*/
public class DynamicSystemService extends IDynamicSystemService.Stub {
private static final String TAG = "DynamicSystemService";
+ private static final long MINIMUM_SD_MB = (30L << 10);
private static final int GSID_ROUGH_TIMEOUT_MS = 8192;
private static final String PATH_DEFAULT = "/data/gsi/";
private Context mContext;
@@ -95,6 +97,13 @@
if (!volume.isMountedWritable()) {
continue;
}
+ DiskInfo disk = volume.getDisk();
+ long mega = disk.size >> 20;
+ Slog.i(TAG, volume.getPath() + ": " + mega + " MB");
+ if (mega < MINIMUM_SD_MB) {
+ Slog.i(TAG, volume.getPath() + ": insufficient storage");
+ continue;
+ }
File sd_internal = volume.getInternalPathForUser(userId);
if (sd_internal != null) {
path = new File(sd_internal, dsuSlot).getPath();
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index b48bc90..81d4b9d 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -48,7 +48,6 @@
import android.net.util.NetdService;
import android.os.Binder;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -64,6 +63,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.net.module.util.NetdUtils;
import libcore.io.IoUtils;
@@ -117,9 +117,6 @@
/* Binder context for this service */
private final Context mContext;
- /* NetworkManager instance */
- private final INetworkManagementService mNetworkManager;
-
/**
* The next non-repeating global ID for tracking resources between users, this service, and
* kernel data structures. Accessing this variable is not thread safe, so it is only read or
@@ -1014,13 +1011,13 @@
*
* @param context Binder context for this service
*/
- private IpSecService(Context context, INetworkManagementService networkManager) {
- this(context, networkManager, IpSecServiceConfiguration.GETSRVINSTANCE);
+ private IpSecService(Context context) {
+ this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
}
- static IpSecService create(Context context, INetworkManagementService networkManager)
+ static IpSecService create(Context context)
throws InterruptedException {
- final IpSecService service = new IpSecService(context, networkManager);
+ final IpSecService service = new IpSecService(context);
service.connectNativeNetdService();
return service;
}
@@ -1034,11 +1031,9 @@
/** @hide */
@VisibleForTesting
- public IpSecService(Context context, INetworkManagementService networkManager,
- IpSecServiceConfiguration config) {
+ public IpSecService(Context context, IpSecServiceConfiguration config) {
this(
context,
- networkManager,
config,
(fd, uid) -> {
try {
@@ -1052,10 +1047,9 @@
/** @hide */
@VisibleForTesting
- public IpSecService(Context context, INetworkManagementService networkManager,
- IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
+ public IpSecService(Context context, IpSecServiceConfiguration config,
+ UidFdTagger uidFdTagger) {
mContext = context;
- mNetworkManager = Objects.requireNonNull(networkManager);
mSrvConfig = config;
mUidFdTagger = uidFdTagger;
}
@@ -1335,7 +1329,7 @@
netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
Binder.withCleanCallingIdentity(() -> {
- mNetworkManager.setInterfaceUp(intfName);
+ NetdUtils.setInterfaceUp(netd, intfName);
});
for (int selAddrFamily : ADDRESS_FAMILIES) {
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index edaf6a90..84e429d 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.Manifest.permission.MANAGE_SENSOR_PRIVACY;
import static android.app.ActivityManager.RunningServiceInfo;
import static android.app.ActivityManager.RunningTaskInfo;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -197,18 +198,20 @@
Intent.EXTRA_USER)).getIdentifier(),
intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
}
- }, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY));
+ }, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY),
+ MANAGE_SENSOR_PRIVACY, null);
}
@Override
- public void onOpStarted(int code, int uid, String packageName,
+ public void onOpStarted(int code, int uid, String packageName, String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
- onOpNoted(code, uid, packageName, flags, result);
+ onOpNoted(code, uid, packageName, attributionTag, flags, result);
}
@Override
public void onOpNoted(int code, int uid, String packageName,
- @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
+ String attributionTag, @AppOpsManager.OpFlags int flags,
+ @AppOpsManager.Mode int result) {
if (result != MODE_ALLOWED || (flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) {
return;
}
@@ -459,12 +462,12 @@
*/
private void enforceSensorPrivacyPermission() {
if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) {
+ MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) {
return;
}
throw new SecurityException(
"Changing sensor privacy requires the following permission: "
- + android.Manifest.permission.MANAGE_SENSOR_PRIVACY);
+ + MANAGE_SENSOR_PRIVACY);
}
/**
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 329ab99..8d5d3d9 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -837,7 +839,10 @@
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (isCallbackPermissioned(cbInfo)) {
- Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode());
+ Binder.withCleanCallingIdentity(
+ () ->
+ cbInfo.mCallback.onVcnStatusChanged(
+ VCN_STATUS_CODE_SAFE_MODE));
}
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a4ff230..2efc83c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -52,6 +52,7 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
+import android.app.ForegroundServiceStartNotAllowedException;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.Notification;
@@ -693,7 +694,7 @@
+ "could not resolve client package " + callingPackage);
}
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
- throw new IllegalStateException(msg);
+ throw new ForegroundServiceStartNotAllowedException(msg);
}
return null;
}
@@ -1778,7 +1779,7 @@
ignoreForeground = true;
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
r.appInfo.uid)) {
- throw new IllegalStateException(msg);
+ throw new ForegroundServiceStartNotAllowedException(msg);
}
}
}
@@ -2111,7 +2112,8 @@
private final AppOpsManager.OnOpNotedListener mOpNotedCallback =
new AppOpsManager.OnOpNotedListener() {
@Override
- public void onOpNoted(int op, int uid, String pkgName, int flags, int result) {
+ public void onOpNoted(int op, int uid, String pkgName,
+ String attributionTag, int flags, int result) {
incrementOpCountIfNeeded(op, uid, result);
}
};
@@ -2119,7 +2121,8 @@
private final AppOpsManager.OnOpStartedListener mOpStartedCallback =
new AppOpsManager.OnOpStartedListener() {
@Override
- public void onOpStarted(int op, int uid, String pkgName, int flags,
+ public void onOpStarted(int op, int uid, String pkgName,
+ String attributionTag, int flags,
int result) {
incrementOpCountIfNeeded(op, uid, result);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5ee0e04..083a963 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -142,6 +142,7 @@
import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityManager;
+import android.app.ActivityManager.PendingIntentInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager.RootTaskInfo;
@@ -173,6 +174,7 @@
import android.app.PendingIntent;
import android.app.ProcessMemoryState;
import android.app.ProfilerInfo;
+import android.app.PropertyInvalidatedCache;
import android.app.WaitResult;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
@@ -181,7 +183,6 @@
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
-import android.compat.Compatibility;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
@@ -306,7 +307,6 @@
import com.android.internal.app.ProcessMap;
import com.android.internal.app.SystemUserHomeActivity;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.content.PackageHelper;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
@@ -4859,19 +4859,6 @@
}
@Override
- public String getPackageForIntentSender(IIntentSender pendingResult) {
- if (!(pendingResult instanceof PendingIntentRecord)) {
- return null;
- }
- try {
- PendingIntentRecord res = (PendingIntentRecord)pendingResult;
- return res.key.packageName;
- } catch (ClassCastException e) {
- }
- return null;
- }
-
- @Override
public void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
mPendingIntentController.registerIntentSenderCancelListener(sender, receiver);
}
@@ -4883,15 +4870,17 @@
}
@Override
- public int getUidForIntentSender(IIntentSender sender) {
+ public PendingIntentInfo getInfoForIntentSender(IIntentSender sender) {
if (sender instanceof PendingIntentRecord) {
- try {
- PendingIntentRecord res = (PendingIntentRecord)sender;
- return res.uid;
- } catch (ClassCastException e) {
- }
+ PendingIntentRecord res = (PendingIntentRecord) sender;
+ return new PendingIntentInfo(
+ res.key.packageName,
+ res.uid,
+ (res.key.flags & PendingIntent.FLAG_IMMUTABLE) != 0,
+ res.key.type);
+ } else {
+ throw new IllegalArgumentException();
}
- return -1;
}
@Override
@@ -4917,15 +4906,6 @@
}
@Override
- public boolean isIntentSenderImmutable(IIntentSender pendingResult) {
- if (pendingResult instanceof PendingIntentRecord) {
- final PendingIntentRecord res = (PendingIntentRecord) pendingResult;
- return (res.key.flags & PendingIntent.FLAG_IMMUTABLE) != 0;
- }
- return false;
- }
-
- @Override
public boolean isIntentSenderAnActivity(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return false;
@@ -4942,33 +4922,6 @@
}
@Override
- public boolean isIntentSenderAForegroundService(IIntentSender pendingResult) {
- if (pendingResult instanceof PendingIntentRecord) {
- final PendingIntentRecord res = (PendingIntentRecord) pendingResult;
- return res.key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE;
- }
- return false;
- }
-
- @Override
- public boolean isIntentSenderAService(IIntentSender pendingResult) {
- if (pendingResult instanceof PendingIntentRecord) {
- final PendingIntentRecord res = (PendingIntentRecord) pendingResult;
- return res.key.type == ActivityManager.INTENT_SENDER_SERVICE;
- }
- return false;
- }
-
- @Override
- public boolean isIntentSenderABroadcast(IIntentSender pendingResult) {
- if (pendingResult instanceof PendingIntentRecord) {
- final PendingIntentRecord res = (PendingIntentRecord) pendingResult;
- return res.key.type == ActivityManager.INTENT_SENDER_BROADCAST;
- }
- return false;
- }
-
- @Override
public Intent getIntentForIntentSender(IIntentSender pendingResult) {
enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT,
"getIntentForIntentSender()");
@@ -6083,10 +6036,18 @@
abiOverride, zygotePolicyFlags);
}
- // TODO: Move to ProcessList?
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) {
+ return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks,
+ false /* disableTestApiChecks */, abiOverride, zygotePolicyFlags);
+ }
+
+ // TODO: Move to ProcessList?
+ @GuardedBy("this")
+ final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
+ boolean disableHiddenApiChecks, boolean disableTestApiChecks,
+ String abiOverride, int zygotePolicyFlags) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -6121,7 +6082,8 @@
mPersistentStartingProcesses.add(app);
mProcessList.startProcessLocked(app, new HostingRecord("added application",
customProcess != null ? customProcess : app.processName),
- zygotePolicyFlags, disableHiddenApiChecks, abiOverride);
+ zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,
+ abiOverride);
}
return app;
@@ -9863,6 +9825,10 @@
if (thread != null) {
pw.println("\n\n** Cache info for pid " + pid + " [" + r.processName + "] **");
pw.flush();
+ if (pid == MY_PID) {
+ PropertyInvalidatedCache.dumpCacheInfo(fd, args);
+ continue;
+ }
try {
TransferPipe tp = new TransferPipe();
try {
@@ -13557,8 +13523,6 @@
if (disableHiddenApiChecks || disableTestApiChecks) {
enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
"disable hidden API checks");
-
- enableTestApiAccess(ai.packageName);
}
final long origId = Binder.clearCallingIdentity();
@@ -13576,8 +13540,8 @@
mUsageStatsService.reportEvent(ii.targetPackage, userId,
UsageEvents.Event.SYSTEM_INTERACTION);
}
- app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride,
- ZYGOTE_POLICY_FLAG_EMPTY);
+ app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
+ disableTestApiChecks, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY);
}
app.setActiveInstrumentation(activeInstr);
@@ -13732,25 +13696,6 @@
app.userId,
"finished inst");
}
-
- disableTestApiAccess(app.info.packageName);
- }
-
- private void enableTestApiAccess(String packageName) {
- if (mPlatformCompat != null) {
- Compatibility.ChangeConfig config = new Compatibility.ChangeConfig(
- Collections.singleton(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */),
- Collections.emptySet());
- CompatibilityChangeConfig override = new CompatibilityChangeConfig(config);
- mPlatformCompat.setOverridesForTest(override, packageName);
- }
- }
-
- private void disableTestApiAccess(String packageName) {
- if (mPlatformCompat != null) {
- mPlatformCompat.clearOverrideForTest(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */,
- packageName);
- }
}
public void finishInstrumentation(IApplicationThread target,
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 7bdf43c..a34163c 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -66,6 +66,10 @@
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5";
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6";
+ @VisibleForTesting static final String KEY_COMPACT_THROTTLE_MIN_OOM_ADJ =
+ "compact_throttle_min_oom_adj";
+ @VisibleForTesting static final String KEY_COMPACT_THROTTLE_MAX_OOM_ADJ =
+ "compact_throttle_max_oom_adj";
@VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE =
"compact_statsd_sample_rate";
@VisibleForTesting static final String KEY_FREEZER_STATSD_SAMPLE_RATE =
@@ -101,6 +105,10 @@
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000;
+ @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ =
+ ProcessList.CACHED_APP_MIN_ADJ;
+ @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ =
+ ProcessList.CACHED_APP_MAX_ADJ;
// The sampling rate to push app compaction events into statsd for upload.
@VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f;
@VisibleForTesting static final long DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB = 12_000L;
@@ -186,6 +194,10 @@
updateFullDeltaRssThrottle();
} else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) {
updateProcStateThrottle();
+ } else if (KEY_COMPACT_THROTTLE_MIN_OOM_ADJ.equals(name)) {
+ updateMinOomAdjThrottle();
+ } else if (KEY_COMPACT_THROTTLE_MAX_OOM_ADJ.equals(name)) {
+ updateMaxOomAdjThrottle();
}
}
}
@@ -217,6 +229,12 @@
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
@GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting volatile long mCompactThrottleMinOomAdj =
+ DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting volatile long mCompactThrottleMaxOomAdj =
+ DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
+ @GuardedBy("mPhenotypeFlagLock")
private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER;
@GuardedBy("this")
@@ -282,6 +300,7 @@
* starts the background thread if necessary.
*/
public void init() {
+ // TODO: initialize flags to default and only update them if values are set in DeviceConfig
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
synchronized (mPhenotypeFlagLock) {
@@ -294,6 +313,8 @@
updateFullDeltaRssThrottle();
updateProcStateThrottle();
updateUseFreezer();
+ updateMinOomAdjThrottle();
+ updateMaxOomAdjThrottle();
}
}
@@ -328,6 +349,8 @@
pw.println(" " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull);
pw.println(" " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS);
pw.println(" " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent);
+ pw.println(" " + KEY_COMPACT_THROTTLE_MIN_OOM_ADJ + "=" + mCompactThrottleMinOomAdj);
+ pw.println(" " + KEY_COMPACT_THROTTLE_MAX_OOM_ADJ + "=" + mCompactThrottleMaxOomAdj);
pw.println(" " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mCompactStatsdSampleRate);
pw.println(" " + KEY_COMPACT_FULL_RSS_THROTTLE_KB + "="
+ mFullAnonRssThrottleKb);
@@ -367,12 +390,23 @@
@GuardedBy("mProcLock")
void compactAppFull(ProcessRecord app) {
- app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
- mPendingCompactionProcesses.add(app);
- mCompactionHandler.sendMessage(
+ // Apply OOM adj score throttle for Full App Compaction.
+ if ((app.mState.getSetAdj() < mCompactThrottleMinOomAdj
+ || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)
+ && app.mState.getCurAdj() >= mCompactThrottleMinOomAdj
+ && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj) {
+ app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
+ mPendingCompactionProcesses.add(app);
+ mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
- COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
-
+ COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
+ } else {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM, "Skipping full compaction for " + app.processName
+ + " oom adj score changed from " + app.mState.getSetAdj()
+ + " to " + app.mState.getCurAdj());
+ }
+ }
}
@GuardedBy("mProcLock")
@@ -629,6 +663,7 @@
@GuardedBy("mPhenotypeFlagLock")
private void updateCompactionThrottles() {
boolean useThrottleDefaults = false;
+ // TODO: improve efficiency by calling DeviceConfig only once for all flags.
String throttleSomeSomeFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_1);
@@ -647,12 +682,20 @@
String throttlePersistentFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_6);
+ String throttleMinOomAdjFlag =
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_COMPACT_THROTTLE_MIN_OOM_ADJ);
+ String throttleMaxOomAdjFlag =
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_COMPACT_THROTTLE_MAX_OOM_ADJ);
if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag)
|| TextUtils.isEmpty(throttleFullSomeFlag)
|| TextUtils.isEmpty(throttleFullFullFlag)
|| TextUtils.isEmpty(throttleBFGSFlag)
- || TextUtils.isEmpty(throttlePersistentFlag)) {
+ || TextUtils.isEmpty(throttlePersistentFlag)
+ || TextUtils.isEmpty(throttleMinOomAdjFlag)
+ || TextUtils.isEmpty(throttleMaxOomAdjFlag)) {
// Set defaults for all if any are not set.
useThrottleDefaults = true;
} else {
@@ -663,6 +706,8 @@
mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);
mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag);
mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag);
+ mCompactThrottleMinOomAdj = Long.parseLong(throttleMinOomAdjFlag);
+ mCompactThrottleMaxOomAdj = Long.parseLong(throttleMaxOomAdjFlag);
} catch (NumberFormatException e) {
useThrottleDefaults = true;
}
@@ -675,6 +720,8 @@
mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
+ mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
+ mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
}
}
@@ -729,6 +776,28 @@
}
}
+ @GuardedBy("mPhenotypeFlagLock")
+ private void updateMinOomAdjThrottle() {
+ mCompactThrottleMinOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
+
+ // Should only compact cached processes.
+ if (mCompactThrottleMinOomAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+ mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
+ }
+ }
+
+ @GuardedBy("mPhenotypeFlagLock")
+ private void updateMaxOomAdjThrottle() {
+ mCompactThrottleMaxOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
+
+ // Should only compact cached processes.
+ if (mCompactThrottleMaxOomAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+ mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
+ }
+ }
+
private boolean parseProcStateThrottle(String procStateThrottleString) {
String[] procStates = TextUtils.split(procStateThrottleString, ",");
mProcStateThrottle.clear();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index d79fb8a..87cba54 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -72,6 +72,7 @@
import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationExitInfo;
@@ -229,6 +230,22 @@
private final ArrayDeque<ProcessRecord> mTmpQueue;
private final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>();
+ /**
+ * Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate
+ * could be called recursively because of the indirect calls during the update;
+ * however the oomAdjUpdate itself doesn't support recursion - in this case we'd
+ * have to queue up the new targets found during the update, and perform another
+ * round of oomAdjUpdate at the end of last update.
+ */
+ @GuardedBy("mService")
+ private boolean mOomAdjUpdateOngoing = false;
+
+ /**
+ * Flag to mark if there is a pending full oomAdjUpdate.
+ */
+ @GuardedBy("mService")
+ private boolean mPendingFullOomAdjUpdate = false;
+
private final PlatformCompatCache mPlatformCompatCache;
private static class PlatformCompatCache {
@@ -439,6 +456,23 @@
if (oomAdjAll && mConstants.OOMADJ_UPDATE_QUICK) {
return updateOomAdjLSP(app, oomAdjReason);
}
+ if (checkAndEnqueueOomAdjTargetLocked(app)) {
+ // Simply return true as there is an oomAdjUpdate ongoing
+ return true;
+ }
+ try {
+ mOomAdjUpdateOngoing = true;
+ return performUpdateOomAdjLSP(app, oomAdjAll, oomAdjReason);
+ } finally {
+ // Kick off the handling of any pending targets enqueued during the above update
+ mOomAdjUpdateOngoing = false;
+ updateOomAdjPendingTargetsLocked(oomAdjReason);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private boolean performUpdateOomAdjLSP(ProcessRecord app, boolean oomAdjAll,
+ String oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
final ProcessStateRecord state = app.mState;
final boolean wasCached = state.isCached();
@@ -453,20 +487,21 @@
? state.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
- boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false,
+ boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp, false,
SystemClock.uptimeMillis());
if (oomAdjAll
&& (wasCached != state.isCached()
|| state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
// Changed to/from cached state, so apps after it in the LRU
// list may also be changed.
- updateOomAdjLSP(oomAdjReason);
+ performUpdateOomAdjLSP(oomAdjReason);
}
+
return success;
}
@GuardedBy({"mService", "mProcLock"})
- private boolean updateOomAdjLSP(ProcessRecord app, int cachedAdj,
+ private boolean performUpdateOomAdjLSP(ProcessRecord app, int cachedAdj,
ProcessRecord TOP_APP, boolean doingAll, long now) {
if (app.getThread() == null) {
return false;
@@ -519,6 +554,22 @@
@GuardedBy({"mService", "mProcLock"})
private void updateOomAdjLSP(String oomAdjReason) {
+ if (checkAndEnqueueOomAdjTargetLocked(null)) {
+ // Simply return as there is an oomAdjUpdate ongoing
+ return;
+ }
+ try {
+ mOomAdjUpdateOngoing = true;
+ performUpdateOomAdjLSP(oomAdjReason);
+ } finally {
+ // Kick off the handling of any pending targets enqueued during the above update
+ mOomAdjUpdateOngoing = false;
+ updateOomAdjPendingTargetsLocked(oomAdjReason);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void performUpdateOomAdjLSP(String oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
// Clear any pending ones because we are doing a full update now.
mPendingProcessSet.clear();
@@ -548,6 +599,23 @@
return true;
}
+ if (checkAndEnqueueOomAdjTargetLocked(app)) {
+ // Simply return true as there is an oomAdjUpdate ongoing
+ return true;
+ }
+
+ try {
+ mOomAdjUpdateOngoing = true;
+ return performUpdateOomAdjLSP(app, oomAdjReason);
+ } finally {
+ // Kick off the handling of any pending targets enqueued during the above update
+ mOomAdjUpdateOngoing = false;
+ updateOomAdjPendingTargetsLocked(oomAdjReason);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private boolean performUpdateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
@@ -567,7 +635,7 @@
state.resetCachedInfo();
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
- boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false,
+ boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp, false,
SystemClock.uptimeMillis());
if (!success || (wasCached == state.isCached() && oldAdj != ProcessList.INVALID_ADJ
&& wasBackground == ActivityManager.isProcStateBackground(
@@ -696,14 +764,59 @@
}
/**
+ * Check if there is an ongoing oomAdjUpdate, enqueue the given process record
+ * to {@link #mPendingProcessSet} if there is one.
+ *
+ * @param app The target app to get an oomAdjUpdate, or a full oomAdjUpdate if it's null.
+ * @return {@code true} if there is an ongoing oomAdjUpdate.
+ */
+ @GuardedBy("mService")
+ private boolean checkAndEnqueueOomAdjTargetLocked(@Nullable ProcessRecord app) {
+ if (!mOomAdjUpdateOngoing) {
+ return false;
+ }
+ if (app != null) {
+ mPendingProcessSet.add(app);
+ } else {
+ mPendingFullOomAdjUpdate = true;
+ }
+ return true;
+ }
+
+ /**
* Kick off an oom adj update pass for the pending targets which are enqueued via
* {@link #enqueueOomAdjTargetLocked}.
*/
@GuardedBy("mService")
void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
+ // First check if there is pending full update
+ if (mPendingFullOomAdjUpdate) {
+ mPendingFullOomAdjUpdate = false;
+ mPendingProcessSet.clear();
+ updateOomAdjLocked(oomAdjReason);
+ return;
+ }
if (mPendingProcessSet.isEmpty()) {
return;
}
+
+ if (mOomAdjUpdateOngoing) {
+ // There's another oomAdjUpdate ongoing, return from here now;
+ // that ongoing update would call us again at the end of it.
+ return;
+ }
+ try {
+ mOomAdjUpdateOngoing = true;
+ performUpdateOomAdjPendingTargetsLocked(oomAdjReason);
+ } finally {
+ // Kick off the handling of any pending targets enqueued during the above update
+ mOomAdjUpdateOngoing = false;
+ updateOomAdjPendingTargetsLocked(oomAdjReason);
+ }
+ }
+
+ @GuardedBy("mService")
+ private void performUpdateOomAdjPendingTargetsLocked(String oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
@@ -1846,8 +1959,8 @@
computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now,
cycleReEval, true);
} else {
- cstate.setCurRawAdj(cstate.getSetAdj());
- cstate.setCurRawProcState(cstate.getSetProcState());
+ cstate.setCurRawAdj(cstate.getCurAdj());
+ cstate.setCurRawProcState(cstate.getCurProcState());
}
int clientAdj = cstate.getCurRawAdj();
@@ -2130,8 +2243,8 @@
if (computeClients) {
computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true);
} else {
- cstate.setCurRawAdj(cstate.getSetAdj());
- cstate.setCurRawProcState(cstate.getSetProcState());
+ cstate.setCurRawAdj(cstate.getCurAdj());
+ cstate.setCurRawProcState(cstate.getCurProcState());
}
if (shouldSkipDueToCycle(state, cstate, procState, adj, cycleReEval)) {
@@ -2428,9 +2541,7 @@
&& (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ
|| state.getCurAdj() == ProcessList.HOME_APP_ADJ)) {
mCachedAppOptimizer.compactAppSome(app);
- } else if ((state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ
- || state.getSetAdj() > ProcessList.CACHED_APP_MAX_ADJ)
- && state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ
+ } else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ
&& state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) {
mCachedAppOptimizer.compactAppFull(app);
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 172f707..cb07a06 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1054,76 +1054,7 @@
}
public static String makeProcStateString(int curProcState) {
- String procState;
- switch (curProcState) {
- case ActivityManager.PROCESS_STATE_PERSISTENT:
- procState = "PER ";
- break;
- case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
- procState = "PERU";
- break;
- case ActivityManager.PROCESS_STATE_TOP:
- procState = "TOP ";
- break;
- case ActivityManager.PROCESS_STATE_BOUND_TOP:
- procState = "BTOP";
- break;
- case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
- procState = "FGS ";
- break;
- case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
- procState = "BFGS";
- break;
- case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
- procState = "IMPF";
- break;
- case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
- procState = "IMPB";
- break;
- case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
- procState = "TRNB";
- break;
- case ActivityManager.PROCESS_STATE_BACKUP:
- procState = "BKUP";
- break;
- case ActivityManager.PROCESS_STATE_SERVICE:
- procState = "SVC ";
- break;
- case ActivityManager.PROCESS_STATE_RECEIVER:
- procState = "RCVR";
- break;
- case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
- procState = "TPSL";
- break;
- case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
- procState = "HVY ";
- break;
- case ActivityManager.PROCESS_STATE_HOME:
- procState = "HOME";
- break;
- case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
- procState = "LAST";
- break;
- case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
- procState = "CAC ";
- break;
- case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
- procState = "CACC";
- break;
- case ActivityManager.PROCESS_STATE_CACHED_RECENT:
- procState = "CRE ";
- break;
- case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
- procState = "CEM ";
- break;
- case ActivityManager.PROCESS_STATE_NONEXISTENT:
- procState = "NONE";
- break;
- default:
- procState = "??";
- break;
- }
- return procState;
+ return ActivityManager.procStateToString(curProcState);
}
public static int makeProcStateProtoEnum(int curProcState) {
@@ -1828,7 +1759,8 @@
*/
@GuardedBy("mService")
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
- int zygotePolicyFlags, boolean disableHiddenApiChecks, String abiOverride) {
+ int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
+ String abiOverride) {
if (app.isPendingStart()) {
return true;
}
@@ -1974,6 +1906,10 @@
throw new IllegalStateException("Invalid API policy: " + policy);
}
runtimeFlags |= policyBits;
+
+ if (disableTestApiChecks) {
+ runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;
+ }
}
String useAppImageCache = SystemProperties.get(
@@ -2437,7 +2373,8 @@
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
int zygotePolicyFlags, String abiOverride) {
return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
- false /* disableHiddenApiChecks */, abiOverride);
+ false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
+ abiOverride);
}
@GuardedBy("mService")
@@ -4768,11 +4705,13 @@
int getBlockStateForUid(UidRecord uidRec) {
// Denotes whether uid's process state is currently allowed network access.
final boolean isAllowed =
- isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState())
+ isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState(),
+ uidRec.getCurCapability())
|| isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState());
// Denotes whether uid's process state was previously allowed network access.
final boolean wasAllowed =
- isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState())
+ isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState(),
+ uidRec.getSetCapability())
|| isProcStateAllowedWhileOnRestrictBackground(uidRec.getSetProcState());
// When the uid is coming to foreground, AMS should inform the app thread that it should
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index e97f0b4..33bdac2 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -23,9 +23,12 @@
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
+import static com.android.server.apphibernation.AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityThread;
import android.app.IActivityManager;
import android.apphibernation.IAppHibernationService;
import android.content.BroadcastReceiver;
@@ -45,6 +48,8 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -93,6 +98,9 @@
private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore;
private final Injector mInjector;
+ @VisibleForTesting
+ boolean mIsServiceEnabled;
+
/**
* Initializes the system service.
* <p>
@@ -139,6 +147,13 @@
initializeGlobalHibernationStates(states);
}
}
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ mIsServiceEnabled = isAppHibernationEnabled();
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_APP_HIBERNATION,
+ ActivityThread.currentApplication().getMainExecutor(),
+ this::onDeviceConfigChanged);
+ }
}
/**
@@ -149,6 +164,10 @@
* @return true if package is hibernating for the user
*/
boolean isHibernatingForUser(String packageName, int userId) {
+ if (!checkHibernationEnabled("isHibernatingForUser")) {
+ return false;
+ }
+
userId = handleIncomingUser(userId, "isHibernating");
if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user "
@@ -174,6 +193,9 @@
* @param packageName package to check
*/
boolean isHibernatingGlobally(String packageName) {
+ if (!checkHibernationEnabled("isHibernatingGlobally")) {
+ return false;
+ }
synchronized (mLock) {
GlobalLevelState state = mGlobalHibernationStates.get(packageName);
if (state == null) {
@@ -192,6 +214,9 @@
* @param isHibernating new hibernation state
*/
void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
+ if (!checkHibernationEnabled("setHibernatingForUser")) {
+ return;
+ }
userId = handleIncomingUser(userId, "setHibernating");
if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
@@ -229,6 +254,9 @@
* @param isHibernating new hibernation state
*/
void setHibernatingGlobally(String packageName, boolean isHibernating) {
+ if (!checkHibernationEnabled("setHibernatingGlobally")) {
+ return;
+ }
synchronized (mLock) {
GlobalLevelState state = mGlobalHibernationStates.get(packageName);
if (state == null) {
@@ -421,6 +449,9 @@
private void onPackageAdded(@NonNull String packageName, int userId) {
synchronized (mLock) {
+ if (!mUserStates.contains(userId)) {
+ return;
+ }
UserLevelState userState = new UserLevelState();
userState.packageName = packageName;
mUserStates.get(userId).put(packageName, userState);
@@ -434,6 +465,9 @@
private void onPackageRemoved(@NonNull String packageName, int userId) {
synchronized (mLock) {
+ if (!mUserStates.contains(userId)) {
+ return;
+ }
mUserStates.get(userId).remove(packageName);
}
}
@@ -444,6 +478,15 @@
}
}
+ private void onDeviceConfigChanged(Properties properties) {
+ for (String key : properties.getKeyset()) {
+ if (TextUtils.equals(KEY_APP_HIBERNATION_ENABLED, key)) {
+ mIsServiceEnabled = isAppHibernationEnabled();
+ break;
+ }
+ }
+ }
+
/**
* Private helper method to get the real user id and enforce permission checks.
*
@@ -461,6 +504,13 @@
}
}
+ private boolean checkHibernationEnabled(String methodName) {
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName));
+ }
+ return mIsServiceEnabled;
+ }
+
private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
static final class AppHibernationServiceStub extends IAppHibernationService.Stub {
@@ -536,7 +586,7 @@
public static boolean isAppHibernationEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_APP_HIBERNATION,
- AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED,
+ KEY_APP_HIBERNATION_ENABLED,
false /* defaultValue */);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index c92abd4..a776458 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3124,7 +3124,7 @@
final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass,
true /* edit */);
if (ops == null) {
- scheduleOpNotedIfNeededLocked(code, uid, packageName, flags,
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_IGNORED);
if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
@@ -3142,7 +3142,7 @@
final UidState uidState = ops.uidState;
if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
attributedOp.rejected(uidState.state, flags);
- scheduleOpNotedIfNeededLocked(code, uid, packageName, flags,
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_IGNORED);
return AppOpsManager.MODE_IGNORED;
}
@@ -3155,7 +3155,8 @@
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
attributedOp.rejected(uidState.state, flags);
- scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, uidMode);
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ uidMode);
return uidMode;
}
} else {
@@ -3167,7 +3168,8 @@
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
attributedOp.rejected(uidState.state, flags);
- scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, mode);
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ mode);
return mode;
}
}
@@ -3177,7 +3179,7 @@
+ packageName + (attributionTag == null ? ""
: "." + attributionTag));
}
- scheduleOpNotedIfNeededLocked(code, uid, packageName, flags,
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_ALLOWED);
attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag, uidState.state,
flags);
@@ -3580,7 +3582,7 @@
final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */);
if (ops == null) {
if (!dryRun) {
- scheduleOpStartedIfNeededLocked(code, uid, packageName,
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, AppOpsManager.MODE_IGNORED);
}
if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
@@ -3590,7 +3592,7 @@
final Op op = getOpLocked(ops, code, uid, true);
if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
if (!dryRun) {
- scheduleOpStartedIfNeededLocked(code, uid, packageName,
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, AppOpsManager.MODE_IGNORED);
}
return AppOpsManager.MODE_IGNORED;
@@ -3611,7 +3613,8 @@
}
if (!dryRun) {
attributedOp.rejected(uidState.state, flags);
- scheduleOpStartedIfNeededLocked(code, uid, packageName, flags, uidMode);
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
+ flags, uidMode);
}
return uidMode;
}
@@ -3626,7 +3629,8 @@
+ packageName);
if (!dryRun) {
attributedOp.rejected(uidState.state, flags);
- scheduleOpStartedIfNeededLocked(code, uid, packageName, flags, mode);
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
+ flags, mode);
}
return mode;
}
@@ -3634,7 +3638,7 @@
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+ " package " + packageName);
if (!dryRun) {
- scheduleOpStartedIfNeededLocked(code, uid, packageName, flags,
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_ALLOWED);
try {
attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
@@ -3774,7 +3778,7 @@
}
private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName,
- @OpFlags int flags, @Mode int result) {
+ String attributionTag, @OpFlags int flags, @Mode int result) {
ArraySet<StartedCallback> dispatchedCallbacks = null;
final int callbackListCount = mStartedWatchers.size();
for (int i = 0; i < callbackListCount; i++) {
@@ -3799,18 +3803,21 @@
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpStarted,
- this, dispatchedCallbacks, code, uid, pkgName, flags, result));
+ this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags,
+ result));
}
private void notifyOpStarted(ArraySet<StartedCallback> callbacks,
- int code, int uid, String packageName, @OpFlags int flags, @Mode int result) {
+ int code, int uid, String packageName, String attributionTag, @OpFlags int flags,
+ @Mode int result) {
final long identity = Binder.clearCallingIdentity();
try {
final int callbackCount = callbacks.size();
for (int i = 0; i < callbackCount; i++) {
final StartedCallback callback = callbacks.valueAt(i);
try {
- callback.mCallback.opStarted(code, uid, packageName, flags, result);
+ callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags,
+ result);
} catch (RemoteException e) {
/* do nothing */
}
@@ -3821,7 +3828,7 @@
}
private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName,
- @OpFlags int flags, @Mode int result) {
+ String attributionTag, @OpFlags int flags, @Mode int result) {
ArraySet<NotedCallback> dispatchedCallbacks = null;
final int callbackListCount = mNotedWatchers.size();
for (int i = 0; i < callbackListCount; i++) {
@@ -3842,11 +3849,13 @@
}
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChecked,
- this, dispatchedCallbacks, code, uid, packageName, flags, result));
+ this, dispatchedCallbacks, code, uid, packageName, attributionTag, flags,
+ result));
}
private void notifyOpChecked(ArraySet<NotedCallback> callbacks,
- int code, int uid, String packageName, @OpFlags int flags, @Mode int result) {
+ int code, int uid, String packageName, String attributionTag, @OpFlags int flags,
+ @Mode int result) {
// There are features watching for checks in our process. The callbacks in
// these features may require permissions our remote caller does not have.
final long identity = Binder.clearCallingIdentity();
@@ -3855,7 +3864,8 @@
for (int i = 0; i < callbackCount; i++) {
final NotedCallback callback = callbacks.valueAt(i);
try {
- callback.mCallback.opNoted(code, uid, packageName, flags, result);
+ callback.mCallback.opNoted(code, uid, packageName, attributionTag, flags,
+ result);
} catch (RemoteException e) {
/* do nothing */
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 29ee8b6..f5b9417 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -726,7 +726,7 @@
// caches the value returned by AudioSystem.isMicrophoneMuted()
private boolean mMicMuteFromSystemCached;
- private boolean mFastScrollSoundEffectsEnabled;
+ private boolean mNavigationRepeatSoundEffectsEnabled;
private boolean mHomeSoundEffectEnabled;
@GuardedBy("mSettingsLock")
@@ -2325,15 +2325,15 @@
VOL_ADJUST_NORMAL);
}
- public void setFastScrollSoundEffectsEnabled(boolean enabled) {
- mFastScrollSoundEffectsEnabled = enabled;
+ public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) {
+ mNavigationRepeatSoundEffectsEnabled = enabled;
}
/**
* @return true if the fast scroll sound effects are enabled
*/
- public boolean areFastScrollSoundEffectsEnabled() {
- return mFastScrollSoundEffectsEnabled;
+ public boolean areNavigationRepeatSoundEffectsEnabled() {
+ return mNavigationRepeatSoundEffectsEnabled;
}
public void setHomeSoundEffectEnabled(boolean enabled) {
@@ -8570,6 +8570,7 @@
public void postDisplaySafeVolumeWarning(int flags) {
if (mController == null)
return;
+ flags = flags | AudioManager.FLAG_SHOW_UI;
try {
mController.displaySafeVolumeWarning(flags);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/audio/SoundEffectsHelper.java b/services/core/java/com/android/server/audio/SoundEffectsHelper.java
index c14bb3e..7031e02 100644
--- a/services/core/java/com/android/server/audio/SoundEffectsHelper.java
+++ b/services/core/java/com/android/server/audio/SoundEffectsHelper.java
@@ -52,6 +52,7 @@
* used by AudioService. As its methods are called on the message handler thread
* of AudioService, the actual work is offloaded to a dedicated thread.
* This helps keeping AudioService responsive.
+ *
* @hide
*/
class SoundEffectsHelper {
@@ -89,15 +90,18 @@
final String mFileName;
int mSampleId;
boolean mLoaded; // for effects in SoundPool
+
Resource(String fileName) {
mFileName = fileName;
mSampleId = EFFECT_NOT_IN_SOUND_POOL;
}
+
void unload() {
mSampleId = EFFECT_NOT_IN_SOUND_POOL;
mLoaded = false;
}
}
+
// All the fields below are accessed by the worker thread exclusively
private final List<Resource> mResources = new ArrayList<Resource>();
private final int[] mEffects = new int[AudioManager.NUM_SOUND_EFFECTS]; // indexes in mResources
@@ -116,9 +120,9 @@
}
/**
- * Unloads samples from the sound pool.
- * This method can be called to free some memory when
- * sound effects are disabled.
+ * Unloads samples from the sound pool.
+ * This method can be called to free some memory when
+ * sound effects are disabled.
*/
/*package*/ void unloadSoundEffects() {
sendMsg(MSG_UNLOAD_EFFECTS, 0, 0, null, 0);
@@ -385,12 +389,12 @@
}
}
- boolean fastScrollSoundEffectsParsed = allFastScrollSoundsParsed(parserCounter);
+ boolean navigationRepeatFxParsed = allNavigationRepeatSoundsParsed(parserCounter);
boolean homeSoundParsed = parserCounter.getOrDefault(AudioManager.FX_HOME, 0) > 0;
- if (fastScrollSoundEffectsParsed || homeSoundParsed) {
+ if (navigationRepeatFxParsed || homeSoundParsed) {
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
- if (audioManager != null && fastScrollSoundEffectsParsed) {
- audioManager.setFastScrollSoundEffectsEnabled(true);
+ if (audioManager != null && navigationRepeatFxParsed) {
+ audioManager.setNavigationRepeatSoundEffectsEnabled(true);
}
if (audioManager != null && homeSoundParsed) {
audioManager.setHomeSoundEffectEnabled(true);
@@ -410,13 +414,13 @@
}
}
- private boolean allFastScrollSoundsParsed(Map<Integer, Integer> parserCounter) {
+ private boolean allNavigationRepeatSoundsParsed(Map<Integer, Integer> parserCounter) {
int numFastScrollSoundEffectsParsed =
- parserCounter.getOrDefault(AudioManager.FX_FAST_SCROLL_1, 0)
- + parserCounter.getOrDefault(AudioManager.FX_FAST_SCROLL_2, 0)
- + parserCounter.getOrDefault(AudioManager.FX_FAST_SCROLL_3, 0)
- + parserCounter.getOrDefault(AudioManager.FX_FAST_SCROLL_4, 0);
- return numFastScrollSoundEffectsParsed == AudioManager.NUM_FAST_SCROLL_SOUND_EFFECTS;
+ parserCounter.getOrDefault(AudioManager.FX_FOCUS_NAVIGATION_REPEAT_1, 0)
+ + parserCounter.getOrDefault(AudioManager.FX_FOCUS_NAVIGATION_REPEAT_2, 0)
+ + parserCounter.getOrDefault(AudioManager.FX_FOCUS_NAVIGATION_REPEAT_3, 0)
+ + parserCounter.getOrDefault(AudioManager.FX_FOCUS_NAVIGATION_REPEAT_4, 0);
+ return numFastScrollSoundEffectsParsed == AudioManager.NUM_NAVIGATION_REPEAT_SOUND_EFFECTS;
}
private int findOrAddResourceByFileName(String fileName) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java
new file mode 100644
index 0000000..f35a520
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java
@@ -0,0 +1,82 @@
+/*
+ * 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.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.internal.R;
+
+public class ReEnrollNotificationUtils {
+
+ private static final String NOTIFICATION_TAG = "FaceService";
+ private static final int NOTIFICATION_ID = 1;
+
+ public static void showReEnrollmentNotification(@NonNull Context context) {
+ final NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+
+ final String name =
+ context.getString(R.string.face_recalibrate_notification_name);
+ final String title =
+ context.getString(R.string.face_recalibrate_notification_title);
+ final String content =
+ context.getString(R.string.face_recalibrate_notification_content);
+
+ final Intent intent = new Intent("android.settings.FACE_SETTINGS");
+ intent.setPackage("com.android.settings");
+
+ final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
+ 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
+ null /* options */, UserHandle.CURRENT);
+
+ final String channelName = "FaceEnrollNotificationChannel";
+
+ final NotificationChannel channel = new NotificationChannel(channelName, name,
+ NotificationManager.IMPORTANCE_HIGH);
+ final Notification notification = new Notification.Builder(context, channelName)
+ .setSmallIcon(R.drawable.ic_lock)
+ .setContentTitle(title)
+ .setContentText(content)
+ .setSubText(name)
+ .setOnlyAlertOnce(true)
+ .setLocalOnly(true)
+ .setAutoCancel(true)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setContentIntent(pendingIntent)
+ .setVisibility(Notification.VISIBILITY_SECRET)
+ .build();
+
+ notificationManager.createNotificationChannel(channel);
+ notificationManager.notifyAsUser(NOTIFICATION_TAG,
+ NOTIFICATION_ID, notification,
+ UserHandle.CURRENT);
+ }
+
+ public static void cancelNotification(@NonNull Context context) {
+ final NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+ notificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, UserHandle.CURRENT);
+ }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 8f55402..089cf1e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -41,6 +41,7 @@
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
import com.android.server.biometrics.sensors.face.UsageStats;
import java.util.ArrayList;
@@ -163,6 +164,9 @@
vibrateError();
}
break;
+ case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL:
+ ReEnrollNotificationUtils.showReEnrollmentNotification(getContext());
+ break;
default:
break;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 898d81b..0eb51fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -41,6 +41,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.face.FaceUtils;
+import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -85,6 +86,13 @@
}
@Override
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
+
+ ReEnrollNotificationUtils.cancelNotification(getContext());
+ }
+
+ @Override
public void destroy() {
try {
AidlNativeHandleUtils.close(mPreviewSurface);
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 ee8823e..1b9bd7f 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
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.NotificationManager;
import android.app.SynchronousUserSwitchObserver;
import android.app.UserSwitchObserver;
import android.content.Context;
@@ -71,6 +70,7 @@
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.face.FaceUtils;
import com.android.server.biometrics.sensors.face.LockoutHalImpl;
+import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
import com.android.server.biometrics.sensors.face.ServiceProvider;
import com.android.server.biometrics.sensors.face.UsageStats;
@@ -96,8 +96,6 @@
private static final String TAG = "Face10";
private static final int ENROLL_TIMEOUT_SEC = 75;
- static final String NOTIFICATION_TAG = "FaceService";
- static final int NOTIFICATION_ID = 1;
private boolean mTestHalEnabled;
@@ -109,7 +107,6 @@
@NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
@NonNull private final LockoutHalImpl mLockoutTracker;
@NonNull private final UsageStats mUsageStats;
- @NonNull private final NotificationManager mNotificationManager;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
@Nullable private IBiometricsFace mDaemon;
@NonNull private final HalResultController mHalResultController;
@@ -343,7 +340,6 @@
mUsageStats = new UsageStats(context);
mAuthenticatorIds = new HashMap<>();
mLazyDaemon = Face10.this::getDaemon;
- mNotificationManager = mContext.getSystemService(NotificationManager.class);
mLockoutTracker = new LockoutHalImpl();
mLockoutResetDispatcher = lockoutResetDispatcher;
mHalResultController = new HalResultController(sensorId, context, mHandler, mScheduler,
@@ -607,8 +603,7 @@
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
- UserHandle.CURRENT);
+ ReEnrollNotificationUtils.cancelNotification(mContext);
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index a4b3ac5..3ca51d3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -17,12 +17,7 @@
package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
@@ -32,7 +27,6 @@
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.R;
@@ -40,6 +34,7 @@
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
import com.android.server.biometrics.sensors.face.UsageStats;
import java.util.ArrayList;
@@ -52,7 +47,7 @@
private static final String TAG = "FaceAuthenticationClient";
- private final NotificationManager mNotificationManager;
+
private final UsageStats mUsageStats;
private final int[] mBiometricPromptIgnoreList;
@@ -72,7 +67,6 @@
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
lockoutTracker, isKeyguard);
- mNotificationManager = context.getSystemService(NotificationManager.class);
mUsageStats = usageStats;
final Resources resources = getContext().getResources();
@@ -188,41 +182,7 @@
mLastAcquire = acquireInfo;
if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) {
- final String name =
- getContext().getString(R.string.face_recalibrate_notification_name);
- final String title =
- getContext().getString(R.string.face_recalibrate_notification_title);
- final String content =
- getContext().getString(R.string.face_recalibrate_notification_content);
-
- final Intent intent = new Intent("android.settings.FACE_SETTINGS");
- intent.setPackage("com.android.settings");
-
- final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(getContext(),
- 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
- null /* options */, UserHandle.CURRENT);
-
- final String channelName = "FaceEnrollNotificationChannel";
-
- NotificationChannel channel = new NotificationChannel(channelName, name,
- NotificationManager.IMPORTANCE_HIGH);
- Notification notification = new Notification.Builder(getContext(), channelName)
- .setSmallIcon(R.drawable.ic_lock)
- .setContentTitle(title)
- .setContentText(content)
- .setSubText(name)
- .setOnlyAlertOnce(true)
- .setLocalOnly(true)
- .setAutoCancel(true)
- .setCategory(Notification.CATEGORY_SYSTEM)
- .setContentIntent(pendingIntent)
- .setVisibility(Notification.VISIBILITY_SECRET)
- .build();
-
- mNotificationManager.createNotificationChannel(channel);
- mNotificationManager.notifyAsUser(Face10.NOTIFICATION_TAG,
- Face10.NOTIFICATION_ID, notification,
- UserHandle.CURRENT);
+ ReEnrollNotificationUtils.showReEnrollmentNotification(getContext());
}
final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 46c49e7..641287f 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import static com.android.net.module.util.CollectionUtils.contains;
+
import android.annotation.NonNull;
import android.net.ConnectivityManager;
import android.net.IDnsResolver;
@@ -33,7 +35,6 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.net.module.util.NetworkStackConstants;
import com.android.server.net.BaseNetworkObserver;
@@ -117,8 +118,8 @@
@VisibleForTesting
protected static boolean requiresClat(NetworkAgentInfo nai) {
// TODO: migrate to NetworkCapabilities.TRANSPORT_*.
- final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
- final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState());
+ final boolean supported = contains(NETWORK_TYPES, nai.networkInfo.getType());
+ final boolean connected = contains(NETWORK_STATES, nai.networkInfo.getState());
// Only run clat on networks that have a global IPv6 address and don't have a native IPv4
// address.
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 8bf1886..9411e33 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -28,6 +28,8 @@
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
+import static com.android.net.module.util.CollectionUtils.toIntArray;
+
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -40,23 +42,21 @@
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.system.OsConstants;
-import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.CollectionUtils;
import com.android.server.LocalServices;
-import com.android.server.SystemConfig;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -80,6 +80,7 @@
private final PackageManager mPackageManager;
private final UserManager mUserManager;
+ private final SystemConfigManager mSystemConfigManager;
private final INetd mNetd;
private final Dependencies mDeps;
@@ -123,6 +124,7 @@
@NonNull final Dependencies deps) {
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mSystemConfigManager = context.getSystemService(SystemConfigManager.class);
mNetd = netd;
mDeps = deps;
}
@@ -174,20 +176,18 @@
mUsers.addAll(mUserManager.getUserHandles(true /* excludeDying */));
- final SparseArray<ArraySet<String>> systemPermission =
- SystemConfig.getInstance().getSystemPermissions();
- for (int i = 0; i < systemPermission.size(); i++) {
- ArraySet<String> perms = systemPermission.valueAt(i);
- int uid = systemPermission.keyAt(i);
- int netdPermission = 0;
- // Get the uids of native services that have UPDATE_DEVICE_STATS or INTERNET permission.
- if (perms != null) {
- netdPermission |= perms.contains(UPDATE_DEVICE_STATS)
- ? INetd.PERMISSION_UPDATE_DEVICE_STATS : 0;
- netdPermission |= perms.contains(INTERNET)
- ? INetd.PERMISSION_INTERNET : 0;
+ final SparseArray<String> netdPermToSystemPerm = new SparseArray<>();
+ netdPermToSystemPerm.put(INetd.PERMISSION_INTERNET, INTERNET);
+ netdPermToSystemPerm.put(INetd.PERMISSION_UPDATE_DEVICE_STATS, UPDATE_DEVICE_STATS);
+ for (int i = 0; i < netdPermToSystemPerm.size(); i++) {
+ final int netdPermission = netdPermToSystemPerm.keyAt(i);
+ final String systemPermission = netdPermToSystemPerm.valueAt(i);
+ final int[] hasPermissionUids =
+ mSystemConfigManager.getSystemPermissionUids(systemPermission);
+ for (int j = 0; j < hasPermissionUids.length; j++) {
+ final int uid = hasPermissionUids[j];
+ netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission);
}
- netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission);
}
log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
update(mUsers, mApps, true);
@@ -204,7 +204,7 @@
if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) {
return false;
}
- final int index = ArrayUtils.indexOf(app.requestedPermissions, permission);
+ final int index = CollectionUtils.indexOf(app.requestedPermissions, permission);
if (index < 0 || index >= app.requestedPermissionsFlags.length) return false;
return (app.requestedPermissionsFlags[index] & REQUESTED_PERMISSION_GRANTED) != 0;
}
@@ -246,15 +246,6 @@
return mApps.containsKey(uid);
}
- private int[] toIntArray(Collection<Integer> list) {
- int[] array = new int[list.size()];
- int i = 0;
- for (Integer item : list) {
- array[i++] = item;
- }
- return array;
- }
-
private void update(Set<UserHandle> users, Map<Integer, Boolean> apps, boolean add) {
List<Integer> network = new ArrayList<>();
List<Integer> system = new ArrayList<>();
@@ -662,23 +653,23 @@
if (allPermissionAppIds.size() != 0) {
mNetd.trafficSetNetPermForUids(
INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
- ArrayUtils.convertToIntArray(allPermissionAppIds));
+ toIntArray(allPermissionAppIds));
}
if (internetPermissionAppIds.size() != 0) {
mNetd.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
- ArrayUtils.convertToIntArray(internetPermissionAppIds));
+ toIntArray(internetPermissionAppIds));
}
if (updateStatsPermissionAppIds.size() != 0) {
mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
- ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
+ toIntArray(updateStatsPermissionAppIds));
}
if (noPermissionAppIds.size() != 0) {
mNetd.trafficSetNetPermForUids(INetd.PERMISSION_NONE,
- ArrayUtils.convertToIntArray(noPermissionAppIds));
+ toIntArray(noPermissionAppIds));
}
if (uninstalledAppIds.size() != 0) {
mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UNINSTALLED,
- ArrayUtils.convertToIntArray(uninstalledAppIds));
+ toIntArray(uninstalledAppIds));
}
} catch (RemoteException e) {
Log.e(TAG, "Pass appId list of special permission failed." + e);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a769e88..01ac81fb 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -113,6 +113,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.NetworkStackConstants;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
@@ -1509,7 +1510,7 @@
if (start != -1) ranges.add(new UidRange(start, stop));
} else if (disallowedApplications != null) {
// Add all ranges for user skipping UIDs for disallowedApplications.
- final UidRange userRange = UidRange.createForUser(userId);
+ final UidRange userRange = UidRange.createForUser(UserHandle.of(userId));
int start = userRange.start;
for (int uid : getAppsUids(disallowedApplications, userId)) {
if (uid == start) {
@@ -1522,7 +1523,7 @@
if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
} else {
// Add all UIDs for the user.
- ranges.add(UidRange.createForUser(userId));
+ ranges.add(UidRange.createForUser(UserHandle.of(userId)));
}
}
@@ -1531,7 +1532,7 @@
private static List<UidRange> uidRangesForUser(int userId, Set<UidRange> existingRanges) {
// UidRange#createForUser returns the entire range of UIDs available to a macro-user.
// This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE}
- final UidRange userRange = UidRange.createForUser(userId);
+ final UidRange userRange = UidRange.createForUser(UserHandle.of(userId));
final List<UidRange> ranges = new ArrayList<>();
for (UidRange range : existingRanges) {
if (userRange.containsRange(range)) {
@@ -2528,7 +2529,7 @@
address /* unused */,
address /* unused */,
network);
- mNms.setInterfaceUp(mTunnelIface.getInterfaceName());
+ NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName());
mSession = mIkev2SessionCreator.createIkeSession(
mContext,
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index fe89903..7b9ca37 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -264,6 +264,10 @@
private final SyncLogger mLogger;
+ // NOTE: this is a temporary allow-list for testing purposes; it will be removed before release.
+ private final String[] mEjSyncAllowedPackages = new String[]{
+ "com.google.android.google", "com.android.frameworks.servicestests"};
+
private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) {
for (JobInfo job: pendingJobs) {
if (job.getId() == jobId) {
@@ -983,6 +987,14 @@
}
}
+ final boolean scheduleAsEj =
+ extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false);
+ // NOTE: this is a temporary check for internal testing - to be removed before release.
+ if (scheduleAsEj && !ArrayUtils.contains(mEjSyncAllowedPackages, callingPackage)) {
+ throw new IllegalArgumentException(
+ callingPackage + " is not allowed to schedule a sync as an EJ yet.");
+ }
+
for (AccountAndUser account : accounts) {
// If userId is specified, do not sync accounts of other users
if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM
@@ -1490,6 +1502,12 @@
+ logSafe(syncOperation.target));
backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE,
SyncStorageEngine.NOT_IN_BACKOFF_MODE);
+ } else {
+ // if an EJ is being backed-off but doesn't have SYNC_EXTRAS_IGNORE_BACKOFF set,
+ // reschedule it as a regular job
+ if (syncOperation.isScheduledAsExpeditedJob()) {
+ syncOperation.scheduleEjAsRegularJob = true;
+ }
}
long now = SystemClock.elapsedRealtime();
long backoffDelay = backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE ? 0
@@ -1640,6 +1658,10 @@
b.setRequiresCharging(true);
}
+ if (syncOperation.isScheduledAsExpeditedJob() && !syncOperation.scheduleEjAsRegularJob) {
+ b.setExpedited(true);
+ }
+
if (syncOperation.syncExemptionFlag
== ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP) {
DeviceIdleInternal dic =
@@ -3951,6 +3973,9 @@
if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) {
return true;
}
+ if (key.equals(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)) {
+ return true;
+ }
if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) {
return true;
}
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 4787635..c8654d1 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -103,6 +103,13 @@
/** Stores the number of times this sync operation failed and had to be retried. */
int retries;
+ /**
+ * Indicates if a sync that was originally scheduled as an EJ is being re-scheduled as a
+ * regular job. Specifically, this will be {@code true} if a sync is being backed-off but
+ * {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF} is not set.
+ */
+ boolean scheduleEjAsRegularJob;
+
/** jobId of the JobScheduler job corresponding to this sync */
public int jobId;
@@ -408,6 +415,12 @@
if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
sb.append(" EXPEDITED");
}
+ if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false)) {
+ sb.append(" EXPEDITED-JOB");
+ if (scheduleEjAsRegularJob) {
+ sb.append("(scheduled-as-regular)");
+ }
+ }
switch (syncExemptionFlag) {
case ContentResolver.SYNC_EXEMPTION_NONE:
break;
@@ -537,6 +550,11 @@
return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, false);
}
+ boolean isScheduledAsExpeditedJob() {
+ return mImmutableExtras.getBoolean(
+ ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false);
+ }
+
boolean isAppStandbyExempted() {
return syncExemptionFlag != ContentResolver.SYNC_EXEMPTION_NONE;
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 225da7a..a2b9b96 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -354,6 +354,10 @@
}
}
+ public void stop() {
+ setLightSensorEnabled(false);
+ }
+
public boolean hasUserDataPoints() {
return mBrightnessMapper.hasUserDataPoints();
}
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index a186e33..06010f5 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -243,7 +243,6 @@
}
/** Stop listening for events */
- @VisibleForTesting
void stop() {
if (DEBUG) {
Slog.d(TAG, "Stop");
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 5e1df27..f212698 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -165,7 +165,7 @@
"Failed to take screenshot because internal display is disconnected");
return false;
}
- boolean isWideColor = SurfaceControl.getActiveColorMode(token)
+ boolean isWideColor = SurfaceControl.getDynamicDisplayInfo(token).activeColorMode
== Display.COLOR_MODE_DISPLAY_P3;
// Set mPrepared here so if initialization fails, resources can be cleaned up.
@@ -817,6 +817,12 @@
}
DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
+ if (displayInfo == null) {
+ // displayInfo can be null if the associated display has been removed. There
+ // is a delay between the display being removed and ColorFade being dismissed.
+ return;
+ }
+
switch (displayInfo.rotation) {
case Surface.ROTATION_0:
t.setPosition(mSurfaceControl, 0, 0);
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index cd17cfe..b3070b7 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import android.content.Context;
import android.graphics.Rect;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
@@ -38,12 +39,14 @@
private final IBinder mDisplayToken;
private final String mUniqueId;
+ protected DisplayDeviceConfig mDisplayDeviceConfig;
// The display device does not manage these properties itself, they are set by
// the display manager service. The display device shouldn't really be looking at these.
private int mCurrentLayerStack = -1;
private int mCurrentOrientation = -1;
private Rect mCurrentLayerStackRect;
private Rect mCurrentDisplayRect;
+ private final Context mContext;
// The display device owns its surface, but it should only set it
// within a transaction from performTraversalLocked.
@@ -53,10 +56,13 @@
// Do not use for any other purpose.
DisplayDeviceInfo mDebugLastLoggedDeviceInfo;
- public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId) {
+ public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId,
+ Context context) {
mDisplayAdapter = displayAdapter;
mDisplayToken = displayToken;
mUniqueId = uniqueId;
+ mDisplayDeviceConfig = null;
+ mContext = context;
}
/**
@@ -74,7 +80,10 @@
* @return The DisplayDeviceConfig; {@code null} if not overridden.
*/
public DisplayDeviceConfig getDisplayDeviceConfig() {
- return null;
+ if (mDisplayDeviceConfig == null) {
+ mDisplayDeviceConfig = loadDisplayDeviceConfig();
+ }
+ return mDisplayDeviceConfig;
}
/**
@@ -292,4 +301,8 @@
pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
pw.println("mCurrentSurface=" + mCurrentSurface);
}
+
+ private DisplayDeviceConfig loadDisplayDeviceConfig() {
+ return DisplayDeviceConfig.create(mContext, false);
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 1b25427..49328f1 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -120,7 +120,20 @@
// If no config can be loaded from any ddc xml at all,
// prepare a whole config using the global config.xml.
// Guaranteed not null
- if (isDefaultDisplay) {
+ return create(context, isDefaultDisplay);
+ }
+
+ /**
+ * Creates an instance using global values since no display device config xml exists.
+ * Uses values from config or PowerManager.
+ *
+ * @param context
+ * @param useConfigXml
+ * @return A configuration instance.
+ */
+ public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
+ DisplayDeviceConfig config;
+ if (useConfigXml) {
config = getConfigFromGlobalXml(context);
} else {
config = getConfigFromPmValues(context);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 501533d..40cee66 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -466,7 +466,7 @@
sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
sb.append(", colorMode ").append(colorMode);
sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes));
- sb.append(", HdrCapabilities ").append(hdrCapabilities);
+ sb.append(", hdrCapabilities ").append(hdrCapabilities);
sb.append(", allmSupported ").append(allmSupported);
sb.append(", gameContentTypeSupported ").append(gameContentTypeSupported);
sb.append(", density ").append(densityDpi);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6a22941..c62dd72 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -249,30 +249,48 @@
/** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */
private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() {
+ // Synchronized to avoid race conditions when updating multiple display states.
@Override
- public void requestDisplayState(int displayId, int state, float brightness) {
- // TODO (b/168210494): Stop applying default display state to all displays.
- if (displayId != Display.DEFAULT_DISPLAY) {
- return;
- }
- final int[] displayIds;
+ public synchronized void requestDisplayState(int displayId, int state, float brightness) {
+ boolean allInactive = true;
+ boolean allOff = true;
+ final boolean stateChanged;
synchronized (mSyncRoot) {
- displayIds = mLogicalDisplayMapper.getDisplayIdsLocked();
+ final int index = mDisplayStates.indexOfKey(displayId);
+ if (index > -1) {
+ final int currentState = mDisplayStates.valueAt(index);
+ stateChanged = state != currentState;
+ if (stateChanged) {
+ final int size = mDisplayStates.size();
+ for (int i = 0; i < size; i++) {
+ final int displayState = i == index ? state : mDisplayStates.valueAt(i);
+ if (displayState != Display.STATE_OFF) {
+ allOff = false;
+ }
+ if (Display.isActiveState(displayState)) {
+ allInactive = false;
+ }
+ if (!allOff && !allInactive) {
+ break;
+ }
+ }
+ }
+ } else {
+ stateChanged = false;
+ }
}
// The order of operations is important for legacy reasons.
if (state == Display.STATE_OFF) {
- for (int id : displayIds) {
- requestDisplayStateInternal(id, state, brightness);
- }
+ requestDisplayStateInternal(displayId, state, brightness);
}
- mDisplayPowerCallbacks.onDisplayStateChange(state);
+ if (stateChanged) {
+ mDisplayPowerCallbacks.onDisplayStateChange(allInactive, allOff);
+ }
if (state != Display.STATE_OFF) {
- for (int id : displayIds) {
- requestDisplayStateInternal(id, state, brightness);
- }
+ requestDisplayStateInternal(displayId, state, brightness);
}
}
};
@@ -1160,7 +1178,7 @@
private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
- mDisplayPowerControllers.delete(displayId);
+ mDisplayPowerControllers.removeReturnOld(displayId).stop();
mDisplayStates.delete(displayId);
mDisplayBrightnesses.delete(displayId);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
@@ -2758,8 +2776,22 @@
public boolean requestPowerState(int groupId, DisplayPowerRequest request,
boolean waitForNegativeProximity) {
synchronized (mSyncRoot) {
- return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
- .requestPowerState(request, waitForNegativeProximity);
+ final DisplayGroup displayGroup = mLogicalDisplayMapper.getDisplayGroupLocked(
+ groupId);
+ if (displayGroup == null) {
+ return true;
+ }
+
+ final int size = displayGroup.getSizeLocked();
+ boolean ready = true;
+ for (int i = 0; i < size; i++) {
+ final DisplayPowerController displayPowerController =
+ mDisplayPowerControllers.get(displayGroup.getIdLocked(i));
+ ready &= displayPowerController.requestPowerState(request,
+ waitForNegativeProximity);
+ }
+
+ return ready;
}
}
@@ -2772,13 +2804,6 @@
}
@Override
- public int getDisplayGroupId(int displayId) {
- synchronized (mSyncRoot) {
- return mLogicalDisplayMapper.getDisplayGroupIdLocked(displayId);
- }
- }
-
- @Override
public void registerDisplayGroupListener(DisplayGroupListener listener) {
mDisplayGroupListeners.add(listener);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 9320f50..62cf86b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -121,6 +121,7 @@
private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6;
private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7;
private static final int MSG_IGNORE_PROXIMITY = 8;
+ private static final int MSG_STOP = 9;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -351,6 +352,7 @@
@Nullable
private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
+ @Nullable
private final ColorDisplayServiceInternal mCdsi;
private final float[] mNitsRange;
@@ -409,6 +411,9 @@
private ObjectAnimator mColorFadeOffAnimator;
private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
+ // True if this DisplayPowerController has been stopped and should no longer be running.
+ private boolean mStopped;
+
/**
* Creates the display power controller.
*/
@@ -583,14 +588,16 @@
DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
DisplayWhiteBalanceController displayWhiteBalanceController = null;
- try {
- displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler);
- displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler,
- mSensorManager, resources);
- displayWhiteBalanceSettings.setCallbacks(this);
- displayWhiteBalanceController.setCallbacks(this);
- } catch (Exception e) {
- Slog.e(TAG, "failed to set up display white-balance: " + e);
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ try {
+ displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler);
+ displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler,
+ mSensorManager, resources);
+ displayWhiteBalanceSettings.setCallbacks(this);
+ displayWhiteBalanceController.setCallbacks(this);
+ } catch (Exception e) {
+ Slog.e(TAG, "failed to set up display white-balance: " + e);
+ }
}
mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
mDisplayWhiteBalanceController = displayWhiteBalanceController;
@@ -602,20 +609,24 @@
mNitsRange = BrightnessMappingStrategy.getFloatArray(context.getResources()
.obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
}
- mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
- boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
- @Override
- public void onReduceBrightColorsActivationChanged(boolean activated) {
- applyReduceBrightColorsSplineAdjustment();
- }
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
+ boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
+ @Override
+ public void onReduceBrightColorsActivationChanged(boolean activated) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
- @Override
- public void onReduceBrightColorsStrengthChanged(int strength) {
+ @Override
+ public void onReduceBrightColorsStrengthChanged(int strength) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ });
+ if (active) {
applyReduceBrightColorsSplineAdjustment();
}
- });
- if (active) {
- applyReduceBrightColorsSplineAdjustment();
+ } else {
+ mCdsi = null;
}
}
@@ -713,6 +724,10 @@
}
synchronized (mLock) {
+ if (mStopped) {
+ return true;
+ }
+
boolean changed = false;
if (waitForNegativeProximity
@@ -731,11 +746,10 @@
if (changed) {
mDisplayReadyLocked = false;
- }
-
- if (changed && !mPendingRequestChangedLocked) {
- mPendingRequestChangedLocked = true;
- sendUpdatePowerStateLocked();
+ if (!mPendingRequestChangedLocked) {
+ mPendingRequestChangedLocked = true;
+ sendUpdatePowerStateLocked();
+ }
}
return mDisplayReadyLocked;
@@ -758,6 +772,34 @@
// TODO: b/175821789 - Support high brightness on multiple (folding) displays
}
+ /**
+ * Unregisters all listeners and interrupts all running threads; halting future work.
+ *
+ * This method should be called when the DisplayPowerController is no longer in use; i.e. when
+ * the {@link #mDisplayId display} has been removed.
+ */
+ public void stop() {
+ synchronized (mLock) {
+ mStopped = true;
+ Message msg = mHandler.obtainMessage(MSG_STOP);
+ mHandler.sendMessage(msg);
+
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setEnabled(false);
+ }
+
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.stop();
+ }
+
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.stop();
+ }
+
+ mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+ }
+ }
+
private void sendUpdatePowerState() {
synchronized (mLock) {
sendUpdatePowerStateLocked();
@@ -765,7 +807,7 @@
}
private void sendUpdatePowerStateLocked() {
- if (!mPendingUpdatePowerStateLocked) {
+ if (!mStopped && !mPendingUpdatePowerStateLocked) {
mPendingUpdatePowerStateLocked = true;
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
mHandler.sendMessage(msg);
@@ -788,7 +830,7 @@
mColorFadeOffAnimator.addListener(mAnimatorListener);
}
- mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
+ mScreenBrightnessRampAnimator = new RampAnimator<>(
mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT);
mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
@@ -829,12 +871,15 @@
}
};
- private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() {
- @Override
- public void onAnimationEnd() {
- sendUpdatePowerState();
- }
- };
+ private final RampAnimator.Listener mRampAnimatorListener = this::sendUpdatePowerState;
+
+ /** Clean up all resources that are accessed via the {@link #mHandler} thread. */
+ private void cleanupHandlerThreadAfterStop() {
+ setProximitySensorEnabled(false);
+ mHandler.removeCallbacksAndMessages(null);
+ mPowerState.stop();
+ mPowerState = null;
+ }
private void updatePowerState() {
// Update the power state request.
@@ -844,6 +889,9 @@
int brightnessAdjustmentFlags = 0;
mBrightnessReasonTemp.set(null);
synchronized (mLock) {
+ if (mStopped) {
+ return;
+ }
mPendingUpdatePowerStateLocked = false;
if (mPendingRequestLocked == null) {
return; // wait until first actual power request
@@ -2144,6 +2192,10 @@
case MSG_IGNORE_PROXIMITY:
ignoreProximitySensorUntilChangedInternal();
break;
+
+ case MSG_STOP:
+ cleanupHandlerThreadAfterStop();
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 1d20d87..77aff5b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -72,6 +72,8 @@
private Runnable mCleanListener;
+ private volatile boolean mStopped;
+
DisplayPowerState(
DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) {
mHandler = new Handler(true /*async*/);
@@ -264,9 +266,24 @@
}
}
+ /**
+ * Interrupts all running threads; halting future work.
+ *
+ * This method should be called when the DisplayPowerState is no longer in use; i.e. when
+ * the {@link #mDisplayId display} has been removed.
+ */
+ public void stop() {
+ mStopped = true;
+ mPhotonicModulator.interrupt();
+ dismissColorFade();
+ mCleanListener = null;
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
public void dump(PrintWriter pw) {
pw.println();
pw.println("Display Power State:");
+ pw.println(" mStopped=" + mStopped);
pw.println(" mScreenState=" + Display.stateToString(mScreenState));
pw.println(" mScreenBrightness=" + mScreenBrightness);
pw.println(" mScreenReady=" + mScreenReady);
@@ -428,7 +445,11 @@
if (!stateChanged && !backlightChanged) {
try {
mLock.wait();
- } catch (InterruptedException ex) { }
+ } catch (InterruptedException ex) {
+ if (mStopped) {
+ return;
+ }
+ }
continue;
}
mActualState = state;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 3b66236..5b2b336 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -38,8 +38,8 @@
import android.view.RoundedCorners;
import android.view.SurfaceControl;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.lights.LightsManager;
@@ -72,6 +72,7 @@
private final Injector mInjector;
+ private final SurfaceControlProxy mSurfaceControlProxy;
// Called with SyncRoot lock held.
public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
@@ -79,10 +80,12 @@
this(syncRoot, context, handler, listener, new Injector());
}
+ @VisibleForTesting
LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener, Injector injector) {
super(syncRoot, context, handler, listener, TAG);
mInjector = injector;
+ mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
}
@Override
@@ -92,58 +95,57 @@
mInjector.setDisplayEventListenerLocked(getHandler().getLooper(),
new LocalDisplayEventListener());
- for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) {
+ for (long physicalDisplayId : mSurfaceControlProxy.getPhysicalDisplayIds()) {
tryConnectDisplayLocked(physicalDisplayId);
}
}
private void tryConnectDisplayLocked(long physicalDisplayId) {
- final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId);
+ final IBinder displayToken =
+ mSurfaceControlProxy.getPhysicalDisplayToken(physicalDisplayId);
if (displayToken != null) {
- SurfaceControl.DisplayInfo info = SurfaceControl.getDisplayInfo(displayToken);
- if (info == null) {
- Slog.w(TAG, "No valid info found for display device " + physicalDisplayId);
+ SurfaceControl.StaticDisplayInfo staticInfo =
+ mSurfaceControlProxy.getStaticDisplayInfo(displayToken);
+ if (staticInfo == null) {
+ Slog.w(TAG, "No valid static info found for display device " + physicalDisplayId);
return;
}
- SurfaceControl.DisplayMode[] displayModes =
- SurfaceControl.getDisplayModes(displayToken);
- if (displayModes == null) {
+ SurfaceControl.DynamicDisplayInfo dynamicInfo =
+ mSurfaceControlProxy.getDynamicDisplayInfo(displayToken);
+ if (dynamicInfo == null) {
+ Slog.w(TAG, "No valid dynamic info found for display device " + physicalDisplayId);
+ return;
+ }
+ if (dynamicInfo.supportedDisplayModes == null) {
// There are no valid modes for this device, so we can't use it
Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId);
return;
}
- int activeDisplayMode = SurfaceControl.getActiveDisplayMode(displayToken);
- if (activeDisplayMode < 0) {
+ if (dynamicInfo.activeDisplayModeId < 0) {
// There is no active mode, and for now we don't have the
// policy to set one.
- Slog.w(TAG, "No active mode found for display device " + physicalDisplayId);
+ Slog.w(TAG, "No valid active mode found for display device " + physicalDisplayId);
return;
}
- int activeColorMode = SurfaceControl.getActiveColorMode(displayToken);
- if (activeColorMode < 0) {
+ if (dynamicInfo.activeColorMode < 0) {
// We failed to get the active color mode. We don't bail out here since on the next
// configuration pass we'll go ahead and set it to whatever it was set to last (or
// COLOR_MODE_NATIVE if this is the first configuration).
- Slog.w(TAG, "Unable to get active color mode for display device " +
- physicalDisplayId);
- activeColorMode = Display.COLOR_MODE_INVALID;
+ Slog.w(TAG, "No valid active color mode for display device " + physicalDisplayId);
+ dynamicInfo.activeColorMode = Display.COLOR_MODE_INVALID;
}
- SurfaceControl.DesiredDisplayModeSpecs modeSpecsSpecs =
- SurfaceControl.getDesiredDisplayModeSpecs(displayToken);
- int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
- Display.HdrCapabilities hdrCapabilities =
- SurfaceControl.getHdrCapabilities(displayToken);
+ SurfaceControl.DesiredDisplayModeSpecs modeSpecs =
+ mSurfaceControlProxy.getDesiredDisplayModeSpecs(displayToken);
LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device == null) {
// Display was added.
final boolean isDefaultDisplay = mDevices.size() == 0;
- device = new LocalDisplayDevice(displayToken, physicalDisplayId, info, displayModes,
- activeDisplayMode, modeSpecsSpecs, colorModes, activeColorMode,
- hdrCapabilities, isDefaultDisplay);
+ device = new LocalDisplayDevice(displayToken, physicalDisplayId, staticInfo,
+ dynamicInfo, modeSpecs, isDefaultDisplay);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else if (device.updateDisplayPropertiesLocked(info, displayModes, activeDisplayMode,
- modeSpecsSpecs, colorModes, activeColorMode, hdrCapabilities)) {
+ } else if (device.updateDisplayPropertiesLocked(staticInfo, dynamicInfo,
+ modeSpecs)) {
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
} else {
@@ -195,7 +197,6 @@
private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
new DisplayModeDirector.DesiredDisplayModeSpecs();
private boolean mDisplayModeSpecsInvalid;
- private int mActiveDisplayModeId;
private int mActiveColorMode;
private Display.HdrCapabilities mHdrCapabilities;
private boolean mAllmSupported;
@@ -204,33 +205,30 @@
private boolean mGameContentTypeRequested;
private boolean mSidekickActive;
private SidekickInternal mSidekickInternal;
- private SurfaceControl.DisplayInfo mDisplayInfo;
- private SurfaceControl.DisplayMode[] mDisplayModes;
+ private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
+ // The supported display modes according in SurfaceFlinger
+ private SurfaceControl.DisplayMode[] mSfDisplayModes;
+ // The active display mode in SurfaceFlinger
+ private SurfaceControl.DisplayMode mActiveSfDisplayMode;
private Spline mSystemBrightnessToNits;
private Spline mNitsToHalBrightness;
- private DisplayDeviceConfig mDisplayDeviceConfig;
private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
new DisplayEventReceiver.FrameRateOverride[0];
LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
- SurfaceControl.DisplayInfo info, SurfaceControl.DisplayMode[] displayModes,
- int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
- int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
- boolean isDefaultDisplay) {
- super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
+ SurfaceControl.StaticDisplayInfo staticDisplayInfo,
+ SurfaceControl.DynamicDisplayInfo dynamicInfo,
+ SurfaceControl.DesiredDisplayModeSpecs modeSpecs, boolean isDefaultDisplay) {
+ super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId,
+ getContext());
mPhysicalDisplayId = physicalDisplayId;
mIsDefaultDisplay = isDefaultDisplay;
- updateDisplayPropertiesLocked(info, displayModes, activeDisplayModeId, modeSpecs,
- colorModes, activeColorMode, hdrCapabilities);
+ updateDisplayPropertiesLocked(staticDisplayInfo, dynamicInfo, modeSpecs);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
- mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay);
- mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
- mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
+ mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay,
+ mSurfaceControlProxy);
mDisplayDeviceConfig = null;
- // Defer configuration file loading
- BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
- LocalDisplayDevice::loadDisplayConfiguration, this));
}
@Override
@@ -241,15 +239,17 @@
/**
* Returns true if there is a change.
**/
- public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info,
- SurfaceControl.DisplayMode[] displayModes,
- int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
- int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) {
+ public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo,
+ SurfaceControl.DynamicDisplayInfo dynamicInfo,
+ SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
boolean changed = updateDisplayModesLocked(
- displayModes, activeDisplayModeId, modeSpecs);
- changed |= updateDisplayInfo(info);
- changed |= updateColorModesLocked(colorModes, activeColorMode);
- changed |= updateHdrCapabilitiesLocked(hdrCapabilities);
+ dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs);
+ changed |= updateStaticInfo(staticInfo);
+ changed |= updateColorModesLocked(dynamicInfo.supportedColorModes,
+ dynamicInfo.activeColorMode);
+ changed |= updateHdrCapabilitiesLocked(dynamicInfo.hdrCapabilities);
+ changed |= updateAllmSupport(dynamicInfo.autoLowLatencyModeSupported);
+ changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported);
if (changed) {
mHavePendingChanges = true;
@@ -260,8 +260,9 @@
public boolean updateDisplayModesLocked(
SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId,
SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
- mDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
- mActiveDisplayModeId = activeDisplayModeId;
+ mSfDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
+ mActiveSfDisplayMode = getModeById(displayModes, activeDisplayModeId);
+
// Build an updated list of all existing modes.
ArrayList<DisplayModeRecord> records = new ArrayList<>();
boolean modesAdded = false;
@@ -282,7 +283,7 @@
// First, check to see if we've already added a matching mode. Since not all
// configuration options are exposed via Display.Mode, it's possible that we have
- // multiple DisplayModess that would generate the same Display.Mode.
+ // multiple DisplayModes that would generate the same Display.Mode.
boolean existingMode = false;
for (DisplayModeRecord record : records) {
if (record.hasMatchingMode(mode)
@@ -312,9 +313,8 @@
// Get the currently active mode
DisplayModeRecord activeRecord = null;
- for (int i = 0; i < records.size(); i++) {
- DisplayModeRecord record = records.get(i);
- if (record.hasMatchingMode(displayModes[activeDisplayModeId])) {
+ for (DisplayModeRecord record : records) {
+ if (record.hasMatchingMode(mActiveSfDisplayMode)) {
activeRecord = record;
break;
}
@@ -340,19 +340,18 @@
// If we can't map the defaultMode index to a mode, then the physical display
// modes must have changed, and the code below for handling changes to the
// list of available modes will take care of updating display mode specs.
- if (activeBaseMode != NO_DISPLAY_MODE_ID) {
- if (mDisplayModeSpecs.baseModeId != activeBaseMode
- || mDisplayModeSpecs.primaryRefreshRateRange.min
- != modeSpecs.primaryRefreshRateMin
- || mDisplayModeSpecs.primaryRefreshRateRange.max
- != modeSpecs.primaryRefreshRateMax
- || mDisplayModeSpecs.appRequestRefreshRateRange.min
- != modeSpecs.appRequestRefreshRateMin
- || mDisplayModeSpecs.appRequestRefreshRateRange.max
- != modeSpecs.appRequestRefreshRateMax) {
- mDisplayModeSpecsInvalid = true;
- sendTraversalRequestLocked();
- }
+ if (activeBaseMode == NO_DISPLAY_MODE_ID
+ || mDisplayModeSpecs.baseModeId != activeBaseMode
+ || mDisplayModeSpecs.primaryRefreshRateRange.min
+ != modeSpecs.primaryRefreshRateMin
+ || mDisplayModeSpecs.primaryRefreshRateRange.max
+ != modeSpecs.primaryRefreshRateMax
+ || mDisplayModeSpecs.appRequestRefreshRateRange.min
+ != modeSpecs.appRequestRefreshRateMin
+ || mDisplayModeSpecs.appRequestRefreshRateRange.max
+ != modeSpecs.appRequestRefreshRateMax) {
+ mDisplayModeSpecsInvalid = true;
+ sendTraversalRequestLocked();
}
}
@@ -370,17 +369,17 @@
// For a new display, we need to initialize the default mode ID.
if (mDefaultModeId == NO_DISPLAY_MODE_ID) {
mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultModeGroup = displayModes[activeDisplayModeId].group;
+ mDefaultModeGroup = mActiveSfDisplayMode.group;
} else if (modesAdded && activeModeChanged) {
Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ "use active mode as default mode.");
mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultModeGroup = displayModes[activeDisplayModeId].group;
+ mDefaultModeGroup = mActiveSfDisplayMode.group;
} else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
Slog.w(TAG, "Default display mode no longer available, using currently"
+ " active mode as default.");
mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultModeGroup = displayModes[activeDisplayModeId].group;
+ mDefaultModeGroup = mActiveSfDisplayMode.group;
}
// Determine whether the display mode specs' base mode is still there.
@@ -410,6 +409,9 @@
@Override
public DisplayDeviceConfig getDisplayDeviceConfig() {
+ if (mDisplayDeviceConfig == null) {
+ loadDisplayConfiguration();
+ }
return mDisplayDeviceConfig;
}
@@ -454,11 +456,11 @@
mSystemBrightnessToNits = sysToNits;
}
- private boolean updateDisplayInfo(SurfaceControl.DisplayInfo info) {
- if (Objects.equals(mDisplayInfo, info)) {
+ private boolean updateStaticInfo(SurfaceControl.StaticDisplayInfo info) {
+ if (Objects.equals(mStaticDisplayInfo, info)) {
return false;
}
- mDisplayInfo = info;
+ mStaticDisplayInfo = info;
return true;
}
@@ -520,6 +522,33 @@
return true;
}
+ private boolean updateAllmSupport(boolean supported) {
+ if (mAllmSupported == supported) {
+ return false;
+ }
+ mAllmSupported = supported;
+ return true;
+ }
+
+ private boolean updateGameContentTypeSupport(boolean supported) {
+ if (mGameContentTypeSupported == supported) {
+ return false;
+ }
+ mGameContentTypeSupported = supported;
+ return true;
+ }
+
+ private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes,
+ int modeId) {
+ for (SurfaceControl.DisplayMode mode : supportedModes) {
+ if (mode.id == modeId) {
+ return mode;
+ }
+ }
+ Slog.e(TAG, "Can't find display mode with id " + modeId);
+ return null;
+ }
+
private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayMode mode,
List<Float> alternativeRefreshRates) {
for (int i = 0; i < mSupportedModes.size(); i++) {
@@ -556,10 +585,9 @@
@Override
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
if (mInfo == null) {
- SurfaceControl.DisplayMode mode = mDisplayModes[mActiveDisplayModeId];
mInfo = new DisplayDeviceInfo();
- mInfo.width = mode.width;
- mInfo.height = mode.height;
+ mInfo.width = mActiveSfDisplayMode.width;
+ mInfo.height = mActiveSfDisplayMode.height;
mInfo.modeId = mActiveModeId;
mInfo.defaultModeId = mDefaultModeId;
mInfo.supportedModes = getDisplayModes(mSupportedModes);
@@ -572,21 +600,21 @@
mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
}
mInfo.hdrCapabilities = mHdrCapabilities;
- mInfo.appVsyncOffsetNanos = mode.appVsyncOffsetNanos;
- mInfo.presentationDeadlineNanos = mode.presentationDeadlineNanos;
+ mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos;
+ mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos;
mInfo.state = mState;
mInfo.uniqueId = getUniqueId();
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
mInfo.address = physicalAddress;
- mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f);
- mInfo.xDpi = mode.xDpi;
- mInfo.yDpi = mode.yDpi;
- mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo;
+ mInfo.densityDpi = (int) (mStaticDisplayInfo.density * 160 + 0.5f);
+ mInfo.xDpi = mActiveSfDisplayMode.xDpi;
+ mInfo.yDpi = mActiveSfDisplayMode.yDpi;
+ mInfo.deviceProductInfo = mStaticDisplayInfo.deviceProductInfo;
// Assume that all built-in displays that have secure output (eg. HDCP) also
// support compositing from gralloc protected buffers.
- if (mDisplayInfo.secure) {
+ if (mStaticDisplayInfo.secure) {
mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
}
@@ -620,7 +648,7 @@
}
}
- if (mDisplayInfo.isInternal) {
+ if (mStaticDisplayInfo.isInternal) {
mInfo.type = Display.TYPE_INTERNAL;
mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
@@ -690,7 +718,12 @@
setDisplayState(Display.STATE_ON);
currentState = Display.STATE_ON;
} else {
- return; // old state and new state is off
+ if (oldState == Display.STATE_UNKNOWN) {
+ // There's no guarantee about what the initial state is
+ // at startup, so we have to set it if previous was UNKNOWN.
+ setDisplayState(state);
+ }
+ return;
}
}
@@ -749,7 +782,7 @@
+ "id=" + physicalDisplayId
+ ", state=" + Display.stateToString(state) + ")");
try {
- SurfaceControl.setDisplayPowerMode(token, mode);
+ mSurfaceControlProxy.setDisplayPowerMode(token, mode);
Trace.traceCounter(Trace.TRACE_TAG_POWER, "DisplayPowerMode", mode);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
@@ -877,10 +910,12 @@
SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
// Do not lock when calling these SurfaceControl methods because they are sync
// operations that may block for a while when setting display power mode.
- SurfaceControl.setDesiredDisplayModeSpecs(displayToken, modeSpecs);
- final int activeMode = SurfaceControl.getActiveDisplayMode(displayToken);
+ mSurfaceControlProxy.setDesiredDisplayModeSpecs(displayToken, modeSpecs);
+
+ final int sfActiveModeId = mSurfaceControlProxy
+ .getDynamicDisplayInfo(displayToken).activeDisplayModeId;
synchronized (getSyncRoot()) {
- if (updateActiveModeLocked(activeMode)) {
+ if (updateActiveModeLocked(sfActiveModeId)) {
updateDeviceInfoLocked();
}
}
@@ -891,8 +926,8 @@
updateDeviceInfoLocked();
}
- public void onActiveDisplayModeChangedLocked(int modeId) {
- if (updateActiveModeLocked(modeId)) {
+ public void onActiveDisplayModeChangedLocked(int sfModeId) {
+ if (updateActiveModeLocked(sfModeId)) {
updateDeviceInfoLocked();
}
}
@@ -904,15 +939,15 @@
}
}
- public boolean updateActiveModeLocked(int activeModeId) {
- if (mActiveDisplayModeId == activeModeId) {
+ public boolean updateActiveModeLocked(int activeSfModeId) {
+ if (mActiveSfDisplayMode.id == activeSfModeId) {
return false;
}
- mActiveDisplayModeId = activeModeId;
- mActiveModeId = findMatchingModeIdLocked(activeModeId);
+ mActiveSfDisplayMode = getModeById(mSfDisplayModes, activeSfModeId);
+ mActiveModeId = findMatchingModeIdLocked(activeSfModeId);
if (mActiveModeId == NO_DISPLAY_MODE_ID) {
Slog.w(TAG, "In unknown mode after setting allowed modes"
- + ", activeModeId=" + mActiveDisplayModeId);
+ + ", activeModeId=" + activeSfModeId);
}
return true;
}
@@ -946,7 +981,7 @@
private void requestColorModeAsync(IBinder displayToken, int colorMode) {
// Do not lock when calling this SurfaceControl method because it is a sync operation
// that may block for a while when setting display power mode.
- SurfaceControl.setActiveColorMode(displayToken, colorMode);
+ mSurfaceControlProxy.setActiveColorMode(displayToken, colorMode);
synchronized (getSyncRoot()) {
updateDeviceInfoLocked();
}
@@ -966,7 +1001,7 @@
return;
}
- SurfaceControl.setAutoLowLatencyMode(getDisplayTokenLocked(), on);
+ mSurfaceControlProxy.setAutoLowLatencyMode(getDisplayTokenLocked(), on);
}
@Override
@@ -983,7 +1018,7 @@
return;
}
- SurfaceControl.setGameContentType(getDisplayTokenLocked(), on);
+ mSurfaceControlProxy.setGameContentType(getDisplayTokenLocked(), on);
}
@Override
@@ -992,7 +1027,6 @@
pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}");
pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid);
- pw.println("mActiveDisplayModeId=" + mActiveDisplayModeId);
pw.println("mActiveModeId=" + mActiveModeId);
pw.println("mActiveColorMode=" + mActiveColorMode);
pw.println("mDefaultModeId=" + mDefaultModeId);
@@ -1003,11 +1037,12 @@
pw.println("mAllmRequested=" + mAllmRequested);
pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported);
pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
- pw.println("mDisplayInfo=" + mDisplayInfo);
- pw.println("mDisplayModes=");
- for (int i = 0; i < mDisplayModes.length; i++) {
- pw.println(" " + mDisplayModes[i]);
+ pw.println("mStaticDisplayInfo=" + mStaticDisplayInfo);
+ pw.println("mSfDisplayModes=");
+ for (int i = 0; i < mSfDisplayModes.length; i++) {
+ pw.println(" " + mSfDisplayModes[i]);
}
+ pw.println("mActiveSfDisplayMode=" + mActiveSfDisplayMode);
pw.println("mSupportedModes=");
for (int i = 0; i < mSupportedModes.size(); i++) {
pw.println(" " + mSupportedModes.valueAt(i));
@@ -1020,17 +1055,16 @@
int matchingModeId = SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID;
DisplayModeRecord record = mSupportedModes.get(modeId);
if (record != null) {
- for (int i = 0; i < mDisplayModes.length; i++) {
- SurfaceControl.DisplayMode mode = mDisplayModes[i];
+ for (SurfaceControl.DisplayMode mode : mSfDisplayModes) {
if (record.hasMatchingMode(mode)) {
if (matchingModeId
== SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID) {
- matchingModeId = i;
+ matchingModeId = mode.id;
}
// Prefer to return a mode that matches the modeGroup
if (mode.group == modeGroup) {
- return i;
+ return mode.id;
}
}
}
@@ -1038,12 +1072,12 @@
return matchingModeId;
}
- private int findMatchingModeIdLocked(int modeId) {
- if (modeId < 0 || modeId >= mDisplayModes.length) {
- Slog.e(TAG, "Invalid display config index " + modeId);
+ private int findMatchingModeIdLocked(int sfModeId) {
+ SurfaceControl.DisplayMode mode = getModeById(mSfDisplayModes, sfModeId);
+ if (mode == null) {
+ Slog.e(TAG, "Invalid display mode ID " + sfModeId);
return NO_DISPLAY_MODE_ID;
}
- SurfaceControl.DisplayMode mode = mDisplayModes[modeId];
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
if (record.hasMatchingMode(mode)) {
@@ -1128,6 +1162,9 @@
public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) {
mReceiver = new ProxyDisplayEventReceiver(looper, listener);
}
+ public SurfaceControlProxy getSurfaceControlProxy() {
+ return new SurfaceControlProxy();
+ }
}
public interface DisplayEventListener {
@@ -1219,24 +1256,79 @@
}
}
+ @VisibleForTesting
+ static class SurfaceControlProxy {
+ public SurfaceControl.DynamicDisplayInfo getDynamicDisplayInfo(IBinder token) {
+ return SurfaceControl.getDynamicDisplayInfo(token);
+ }
+
+ public long[] getPhysicalDisplayIds() {
+ return SurfaceControl.getPhysicalDisplayIds();
+ }
+
+ public IBinder getPhysicalDisplayToken(long physicalDisplayId) {
+ return SurfaceControl.getPhysicalDisplayToken(physicalDisplayId);
+ }
+
+ public SurfaceControl.StaticDisplayInfo getStaticDisplayInfo(IBinder displayToken) {
+ return SurfaceControl.getStaticDisplayInfo(displayToken);
+ }
+
+ public SurfaceControl.DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(
+ IBinder displayToken) {
+ return SurfaceControl.getDesiredDisplayModeSpecs(displayToken);
+ }
+
+ public boolean setDesiredDisplayModeSpecs(IBinder token,
+ SurfaceControl.DesiredDisplayModeSpecs specs) {
+ return SurfaceControl.setDesiredDisplayModeSpecs(token, specs);
+ }
+
+ public void setDisplayPowerMode(IBinder displayToken, int mode) {
+ SurfaceControl.setDisplayPowerMode(displayToken, mode);
+ }
+
+ public boolean setActiveColorMode(IBinder displayToken, int colorMode) {
+ return SurfaceControl.setActiveColorMode(displayToken, colorMode);
+ }
+
+ public void setAutoLowLatencyMode(IBinder displayToken, boolean on) {
+ SurfaceControl.setAutoLowLatencyMode(displayToken, on);
+
+ }
+
+ public void setGameContentType(IBinder displayToken, boolean on) {
+ SurfaceControl.setGameContentType(displayToken, on);
+ }
+
+ public boolean getDisplayBrightnessSupport(IBinder displayToken) {
+ return SurfaceControl.getDisplayBrightnessSupport(displayToken);
+ }
+
+ public boolean setDisplayBrightness(IBinder displayToken, float brightness) {
+ return SurfaceControl.setDisplayBrightness(displayToken, brightness);
+ }
+ }
+
static class BacklightAdapter {
private final IBinder mDisplayToken;
private final LogicalLight mBacklight;
private final boolean mUseSurfaceControlBrightness;
+ private final SurfaceControlProxy mSurfaceControlProxy;
private boolean mForceSurfaceControl = false;
/**
* @param displayToken Token for display associated with this backlight.
* @param isDefaultDisplay {@code true} if it is the default display.
- * @param forceSurfaceControl {@code true} if brightness should always be
- * set via SurfaceControl API.
*/
- BacklightAdapter(IBinder displayToken, boolean isDefaultDisplay) {
+ BacklightAdapter(IBinder displayToken, boolean isDefaultDisplay,
+ SurfaceControlProxy surfaceControlProxy) {
mDisplayToken = displayToken;
+ mSurfaceControlProxy = surfaceControlProxy;
- mUseSurfaceControlBrightness =
- SurfaceControl.getDisplayBrightnessSupport(mDisplayToken);
+ mUseSurfaceControlBrightness = mSurfaceControlProxy
+ .getDisplayBrightnessSupport(mDisplayToken);
if (!mUseSurfaceControlBrightness && isDefaultDisplay) {
LightsManager lights = LocalServices.getService(LightsManager.class);
@@ -1248,7 +1340,7 @@
void setBrightness(float brightness) {
if (mUseSurfaceControlBrightness || mForceSurfaceControl) {
- SurfaceControl.setDisplayBrightness(mDisplayToken, brightness);
+ mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, brightness);
} else if (mBacklight != null) {
mBacklight.setBrightness(brightness);
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index a054db5..a3ff534 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -17,7 +17,6 @@
package com.android.server.display;
import android.content.Context;
-import android.os.Process;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
@@ -143,10 +142,6 @@
return null;
}
- public int[] getDisplayIdsLocked() {
- return getDisplayIdsLocked(Process.SYSTEM_UID);
- }
-
public int[] getDisplayIdsLocked(int callingUid) {
final int count = mLogicalDisplays.size();
int[] displayIds = new int[count];
@@ -171,15 +166,6 @@
}
}
- public int getDisplayGroupIdLocked(int displayId) {
- final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId);
- if (displayGroup != null) {
- return displayGroup.getGroupId();
- }
-
- return -1;
- }
-
public DisplayGroup getDisplayGroupLocked(int groupId) {
final int size = mDisplayIdToGroupMap.size();
for (int i = 0; i < size; i++) {
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 69943e3..330379c 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -281,7 +281,8 @@
List<OverlayMode> modes, int activeMode, int defaultMode,
float refreshRate, long presentationDeadlineNanos,
OverlayFlags flags, int state, SurfaceTexture surfaceTexture, int number) {
- super(OverlayDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + number);
+ super(OverlayDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + number,
+ getContext());
mName = name;
mRefreshRate = refreshRate;
mDisplayPresentationDeadlineNanos = presentationDeadlineNanos;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index ff4717b..52a810b 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -236,7 +236,7 @@
int ownerUid, String ownerPackageName, Surface surface, int flags,
Callback callback, String uniqueId, int uniqueIndex,
VirtualDisplayConfig virtualDisplayConfig) {
- super(VirtualDisplayAdapter.this, displayToken, uniqueId);
+ super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext());
mAppToken = appToken;
mOwnerUid = ownerUid;
mOwnerPackageName = ownerPackageName;
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 5732317..d2baaf22 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -598,7 +598,8 @@
public WifiDisplayDevice(IBinder displayToken, String name,
int width, int height, float refreshRate, int flags, String address,
Surface surface) {
- super(WifiDisplayAdapter.this, displayToken, DISPLAY_NAME_PREFIX + address);
+ super(WifiDisplayAdapter.this, displayToken, DISPLAY_NAME_PREFIX + address,
+ getContext());
mName = name;
mWidth = width;
mHeight = height;
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index 86a8e36..983b6b5 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -100,9 +100,9 @@
@Override
public boolean start() {
- if (mIsCec20) {
- sendSetStreamPath();
- }
+ // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0.
+ // The message is re-sent at the end of the action for devices that don't support 2.0.
+ sendSetStreamPath();
int targetPowerStatus = localDevice().mService.getHdmiCecNetwork()
.getCecDeviceInfo(getTargetAddress()).getDevicePowerStatus();
if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index f64efe7..624af30 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -39,6 +39,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ConcurrentUtils;
import com.android.server.hdmi.cec.config.CecSettings;
import com.android.server.hdmi.cec.config.Setting;
import com.android.server.hdmi.cec.config.Value;
@@ -55,7 +56,9 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.Executor;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -99,7 +102,7 @@
private final Object mLock = new Object();
@GuardedBy("mLock")
- private final ArrayMap<Setting, Set<SettingChangeListener>>
+ private final ArrayMap<Setting, ArrayMap<SettingChangeListener, Executor>>
mSettingChangeListeners = new ArrayMap<>();
private SettingsObserver mSettingsObserver;
@@ -292,7 +295,7 @@
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
- return STORAGE_GLOBAL_SETTINGS;
+ return STORAGE_SHARED_PREFS;
case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
@@ -329,7 +332,7 @@
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
return Global.HDMI_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
- return Global.HDMI_CEC_VERSION;
+ return setting.getName();
case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
@@ -402,9 +405,6 @@
case Global.HDMI_CONTROL_ENABLED:
notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
break;
- case Global.HDMI_CEC_VERSION:
- notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION);
- break;
case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP:
notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
break;
@@ -430,12 +430,20 @@
private void notifySettingChanged(@NonNull Setting setting) {
synchronized (mLock) {
- Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting);
+ ArrayMap<SettingChangeListener, Executor> listeners =
+ mSettingChangeListeners.get(setting);
if (listeners == null) {
return; // No listeners registered, do nothing.
}
- for (SettingChangeListener listener: listeners) {
- listener.onChange(setting.getName());
+ for (Entry<SettingChangeListener, Executor> entry: listeners.entrySet()) {
+ SettingChangeListener listener = entry.getKey();
+ Executor executor = entry.getValue();
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ listener.onChange(setting.getName());
+ }
+ });
}
}
}
@@ -450,7 +458,6 @@
ContentResolver resolver = mContext.getContentResolver();
String[] settings = new String[] {
Global.HDMI_CONTROL_ENABLED,
- Global.HDMI_CEC_VERSION,
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
@@ -471,10 +478,19 @@
}
/**
- * Register change listener for a given setting name.
+ * Register change listener for a given setting name using DirectExecutor.
*/
public void registerChangeListener(@NonNull @CecSettingName String name,
SettingChangeListener listener) {
+ registerChangeListener(name, listener, ConcurrentUtils.DIRECT_EXECUTOR);
+ }
+
+ /**
+ * Register change listener for a given setting name and executor.
+ */
+ public void registerChangeListener(@NonNull @CecSettingName String name,
+ SettingChangeListener listener,
+ Executor executor) {
Setting setting = getSetting(name);
if (setting == null) {
throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -486,9 +502,9 @@
}
synchronized (mLock) {
if (!mSettingChangeListeners.containsKey(setting)) {
- mSettingChangeListeners.put(setting, new HashSet<>());
+ mSettingChangeListeners.put(setting, new ArrayMap<>());
}
- mSettingChangeListeners.get(setting).add(listener);
+ mSettingChangeListeners.get(setting).put(listener, executor);
}
}
@@ -503,7 +519,8 @@
}
synchronized (mLock) {
if (mSettingChangeListeners.containsKey(setting)) {
- Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting);
+ ArrayMap<SettingChangeListener, Executor> listeners =
+ mSettingChangeListeners.get(setting);
listeners.remove(listener);
if (listeners.isEmpty()) {
mSettingChangeListeners.remove(setting);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index d014f14..b4d9b01 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -108,6 +108,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.stream.Collectors;
/**
@@ -199,6 +200,13 @@
public @interface WakeReason {
}
+ private final Executor mServiceThreadExecutor = new Executor() {
+ @Override
+ public void execute(Runnable r) {
+ runOnServiceThread(r);
+ }
+ };
+
// Logical address of the active source.
@GuardedBy("mLock")
protected final ActiveSource mActiveSource = new ActiveSource();
@@ -520,14 +528,14 @@
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
setControlEnabled(enabled);
}
- });
+ }, mServiceThreadExecutor);
mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
new HdmiCecConfig.SettingChangeListener() {
@Override
public void onChange(String setting) {
initializeCec(INITIATED_BY_ENABLE_CEC);
}
- });
+ }, mServiceThreadExecutor);
mHdmiCecConfig.registerChangeListener(
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
new HdmiCecConfig.SettingChangeListener() {
@@ -537,7 +545,7 @@
setCecOption(OptionKey.WAKEUP, tv().getAutoWakeup());
}
}
- });
+ }, mServiceThreadExecutor);
}
private void bootCompleted() {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index c5be20e..bbe52bc 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.input;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -51,6 +52,8 @@
import android.hardware.input.InputSensorInfo;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Bundle;
@@ -237,9 +240,21 @@
// List of vibrator states by device id.
@GuardedBy("mVibratorLock")
private final SparseBooleanArray mIsVibrating = new SparseBooleanArray();
+ private Object mLightLock = new Object();
+ // State for light tokens. A light token marks a lights manager session, it is generated
+ // by light session open() and deleted in session close().
+ // When lights session requests light states, the token will be used to find the light session.
+ @GuardedBy("mLightLock")
+ private final ArrayMap<IBinder, LightSession> mLightSessions =
+ new ArrayMap<IBinder, LightSession>();
// State for lid switch
+ // Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events
+ // are delivered in order. For ex, when a new lid switch callback is registered the lock is held
+ // while the callback is processing the initial lid switch event which guarantees that any
+ // events that occur at the same time are delivered after the callback has returned.
private final Object mLidSwitchLock = new Object();
+ @GuardedBy("mLidSwitchLock")
private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
// State for the currently installed input filter.
@@ -303,6 +318,12 @@
private static native int[] nativeGetVibratorIds(long ptr, int deviceId);
private static native int nativeGetBatteryCapacity(long ptr, int deviceId);
private static native int nativeGetBatteryStatus(long ptr, int deviceId);
+ private static native List<Light> nativeGetLights(long ptr, int deviceId);
+ private static native int nativeGetLightPlayerId(long ptr, int deviceId, int lightId);
+ private static native int nativeGetLightColor(long ptr, int deviceId, int lightId);
+ private static native void nativeSetLightPlayerId(long ptr, int deviceId, int lightId,
+ int playerId);
+ private static native void nativeSetLightColor(long ptr, int deviceId, int lightId, int color);
private static native void nativeReloadKeyboardLayouts(long ptr);
private static native void nativeReloadDeviceAliases(long ptr);
private static native String nativeDump(long ptr);
@@ -387,9 +408,6 @@
public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
- /** Indicates an open state for the lid switch. */
- public static final int SW_STATE_LID_OPEN = 0;
-
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
final boolean mUseDevInputEventForAudioJack;
@@ -425,13 +443,18 @@
}
void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
- boolean lidOpen;
synchronized (mLidSwitchLock) {
mLidSwitchCallbacks.add(callback);
- lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
- == SW_STATE_LID_OPEN;
+
+ // Skip triggering the initial callback if the system is not yet ready as the switch
+ // state will be reported as KEY_STATE_UNKNOWN. The callback will be triggered in
+ // systemRunning().
+ if (mSystemReady) {
+ boolean lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
+ == KEY_STATE_UP;
+ callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
+ }
}
- callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
}
void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
@@ -479,7 +502,18 @@
}
mNotificationManager = (NotificationManager)mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
- mSystemReady = true;
+
+ synchronized (mLidSwitchLock) {
+ mSystemReady = true;
+
+ // Send the initial lid switch state to any callback registered before the system was
+ // ready.
+ int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID);
+ for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
+ LidSwitchCallback callback = mLidSwitchCallbacks.get(i);
+ callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP);
+ }
+ }
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -2294,6 +2328,151 @@
}
}
+ /**
+ * LightSession represents a light session for lights manager.
+ */
+ private final class LightSession implements DeathRecipient {
+ private final int mDeviceId;
+ private final IBinder mToken;
+ private final String mOpPkg;
+ // The light ids and states that are requested by the light seesion
+ private int[] mLightIds;
+ private LightState[] mLightStates;
+
+ LightSession(int deviceId, String opPkg, IBinder token) {
+ mDeviceId = deviceId;
+ mOpPkg = opPkg;
+ mToken = token;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Light token died.");
+ }
+ synchronized (mLightLock) {
+ closeLightSession(mDeviceId, mToken);
+ mLightSessions.remove(mToken);
+ }
+ }
+ }
+
+ /**
+ * Returns the lights available for apps to control on the specified input device.
+ * Only lights that aren't reserved for system use are available to apps.
+ */
+ @Override // Binder call
+ public List<Light> getLights(int deviceId) {
+ return nativeGetLights(mPtr, deviceId);
+ }
+
+ /**
+ * Set specified light state with for a specific input device.
+ */
+ private void setLightStateInternal(int deviceId, Light light, LightState lightState) {
+ Preconditions.checkNotNull(light, "light does not exist");
+ if (DEBUG) {
+ Slog.d(TAG, "setLightStateInternal device " + deviceId + " light " + light
+ + "lightState " + lightState);
+ }
+ if (light.getType() == Light.LIGHT_TYPE_INPUT_PLAYER_ID) {
+ nativeSetLightPlayerId(mPtr, deviceId, light.getId(), lightState.getPlayerId());
+ } else if (light.getType() == Light.LIGHT_TYPE_INPUT_SINGLE
+ || light.getType() == Light.LIGHT_TYPE_INPUT_RGB) {
+ // Set ARGB format color to input device light
+ // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color
+ nativeSetLightColor(mPtr, deviceId, light.getId(), lightState.getColor());
+ } else {
+ Slog.e(TAG, "setLightStates for unsupported light type " + light.getType());
+ }
+ }
+
+ /**
+ * Set multiple light states with multiple light ids for a specific input device.
+ */
+ private void setLightStatesInternal(int deviceId, int[] lightIds, LightState[] lightStates) {
+ final List<Light> lights = nativeGetLights(mPtr, deviceId);
+ SparseArray<Light> lightArray = new SparseArray<>();
+ for (int i = 0; i < lights.size(); i++) {
+ lightArray.put(lights.get(i).getId(), lights.get(i));
+ }
+ for (int i = 0; i < lightIds.length; i++) {
+ if (lightArray.contains(lightIds[i])) {
+ setLightStateInternal(deviceId, lightArray.get(lightIds[i]), lightStates[i]);
+ }
+ }
+ }
+
+ /**
+ * Set states for multiple lights for an opened light session.
+ */
+ @Override
+ public void setLightStates(int deviceId, int[] lightIds, LightState[] lightStates,
+ IBinder token) {
+ Preconditions.checkArgument(lightIds.length == lightStates.length,
+ "lights and light states are not same length");
+ synchronized (mLightLock) {
+ LightSession lightSession = mLightSessions.get(token);
+ Preconditions.checkArgument(lightSession != null, "not registered");
+ Preconditions.checkState(lightSession.mDeviceId == deviceId, "Incorrect device ID");
+ lightSession.mLightIds = lightIds.clone();
+ lightSession.mLightStates = lightStates.clone();
+ if (DEBUG) {
+ Slog.d(TAG, "setLightStates for " + lightSession.mOpPkg + " device " + deviceId);
+ }
+ }
+ setLightStatesInternal(deviceId, lightIds, lightStates);
+ }
+
+ @Override
+ public @Nullable LightState getLightState(int deviceId, int lightId) {
+ synchronized (mLightLock) {
+ int color = nativeGetLightColor(mPtr, deviceId, lightId);
+ int playerId = nativeGetLightPlayerId(mPtr, deviceId, lightId);
+
+ return new LightState(color, playerId);
+ }
+ }
+
+ @Override
+ public void openLightSession(int deviceId, String opPkg, IBinder token) {
+ Preconditions.checkNotNull(token);
+ synchronized (mLightLock) {
+ Preconditions.checkState(mLightSessions.get(token) == null, "already registered");
+ LightSession lightSession = new LightSession(deviceId, opPkg, token);
+ try {
+ token.linkToDeath(lightSession, 0);
+ } catch (RemoteException ex) {
+ // give up
+ ex.rethrowAsRuntimeException();
+ }
+ mLightSessions.put(token, lightSession);
+ if (DEBUG) {
+ Slog.d(TAG, "Open light session for " + opPkg + " device " + deviceId);
+ }
+ }
+ }
+
+ @Override
+ public void closeLightSession(int deviceId, IBinder token) {
+ Preconditions.checkNotNull(token);
+ synchronized (mLightLock) {
+ LightSession lightSession = mLightSessions.get(token);
+ Preconditions.checkState(lightSession != null, "not registered");
+ // Turn off the lights that were previously requested by the session to be closed.
+ Arrays.fill(lightSession.mLightStates, new LightState(0));
+ setLightStatesInternal(deviceId, lightSession.mLightIds,
+ lightSession.mLightStates);
+ mLightSessions.remove(token);
+ // If any other session is still pending with light request, apply the first session's
+ // request.
+ if (!mLightSessions.isEmpty()) {
+ LightSession nextSession = mLightSessions.valueAt(0);
+ setLightStatesInternal(deviceId, nextSession.mLightIds, nextSession.mLightStates);
+ }
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -2379,14 +2558,13 @@
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
-
- ArrayList<LidSwitchCallback> callbacksCopy;
synchronized (mLidSwitchLock) {
- callbacksCopy = new ArrayList<>(mLidSwitchCallbacks);
- }
- for (int i = 0; i < callbacksCopy.size(); i++) {
- LidSwitchCallback callbacks = callbacksCopy.get(i);
- callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
+ if (mSystemReady) {
+ for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
+ LidSwitchCallback callbacks = mLidSwitchCallbacks.get(i);
+ callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/input/OWNERS b/services/core/java/com/android/server/input/OWNERS
index 0313a40..82c6ee1 100644
--- a/services/core/java/com/android/server/input/OWNERS
+++ b/services/core/java/com/android/server/input/OWNERS
@@ -1,2 +1,3 @@
+lzye@google.com
michaelwr@google.com
svv@google.com
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0754df0..1e66589 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2620,8 +2620,9 @@
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
// Dispatch display id for InputMethodService to update context display.
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
- MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken));
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(
+ MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken,
+ mMethodMap.get(mCurMethodId).getConfigChanges()));
scheduleNotifyImeUidToAudioService(mCurMethodUid);
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
@@ -4466,7 +4467,8 @@
}
final IBinder token = (IBinder) args.arg2;
((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
- new InputMethodPrivilegedOperationsImpl(this, token));
+ new InputMethodPrivilegedOperationsImpl(this, token),
+ (int) args.arg3);
} catch (RemoteException e) {
}
args.recycle();
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 6deb19a..cbbc981 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -70,6 +70,7 @@
import android.location.provider.ProviderProperties;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.ICancellationSignal;
import android.os.ParcelFileDescriptor;
@@ -251,7 +252,7 @@
// @GuardedBy("mProviderManagers")
// hold lock for writes, no lock necessary for simple reads
- private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
+ final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
new CopyOnWriteArrayList<>();
LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) {
@@ -283,7 +284,7 @@
}
@Nullable
- private LocationProviderManager getLocationProviderManager(String providerName) {
+ LocationProviderManager getLocationProviderManager(String providerName) {
if (providerName == null) {
return null;
}
@@ -350,6 +351,22 @@
void onSystemReady() {
mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
this::onLocationModeChanged);
+
+ if (Build.IS_DEBUGGABLE) {
+ // on debug builds, watch for location noteOps while location is off. there are some
+ // scenarios (emergency location) where this is expected, but generally this should
+ // rarely occur, and may indicate bugs. dump occurrences to logs for further evaluation
+ AppOpsManager appOps = Objects.requireNonNull(
+ mContext.getSystemService(AppOpsManager.class));
+ appOps.startWatchingNoted(
+ new int[]{AppOpsManager.OP_FINE_LOCATION, AppOpsManager.OP_COARSE_LOCATION},
+ (code, uid, packageName, attributionTag, flags, result) -> {
+ if (!isLocationEnabledForUser(UserHandle.getUserId(uid))) {
+ Log.w(TAG, "location noteOp with location off - "
+ + CallerIdentity.forTest(uid, 0, packageName, attributionTag));
+ }
+ });
+ }
}
void onSystemThirdPartyAppsCanStart() {
@@ -479,7 +496,7 @@
@Override
public void startGnssBatch(long periodNanos, ILocationListener listener, String packageName,
- String attributionTag, String listenerId) {
+ @Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
if (mGnssManagerService == null) {
@@ -616,7 +633,7 @@
@Nullable
@Override
public ICancellationSignal getCurrentLocation(String provider, LocationRequest request,
- ILocationCallback consumer, String packageName, String attributionTag,
+ ILocationCallback consumer, String packageName, @Nullable String attributionTag,
String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
@@ -640,7 +657,7 @@
@Override
public void registerLocationListener(String provider, LocationRequest request,
ILocationListener listener, String packageName, @Nullable String attributionTag,
- @Nullable String listenerId) {
+ String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
@@ -791,7 +808,7 @@
@Override
public Location getLastLocation(String provider, LastLocationRequest request,
- String packageName, String attributionTag) {
+ String packageName, @Nullable String attributionTag) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
identity.getPid());
@@ -858,9 +875,10 @@
@Override
public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
- String attributionTag) {
+ @Nullable String attributionTag, String listenerId) {
if (mGnssManagerService != null) {
- mGnssManagerService.registerGnssStatusCallback(listener, packageName, attributionTag);
+ mGnssManagerService.registerGnssStatusCallback(listener, packageName, attributionTag,
+ listenerId);
}
}
@@ -873,9 +891,10 @@
@Override
public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName,
- String attributionTag) {
+ @Nullable String attributionTag, String listenerId) {
if (mGnssManagerService != null) {
- mGnssManagerService.registerGnssNmeaCallback(listener, packageName, attributionTag);
+ mGnssManagerService.registerGnssNmeaCallback(listener, packageName, attributionTag,
+ listenerId);
}
}
@@ -887,11 +906,12 @@
}
@Override
- public void addGnssMeasurementsListener(@Nullable GnssMeasurementRequest request,
- IGnssMeasurementsListener listener, String packageName, String attributionTag) {
+ public void addGnssMeasurementsListener(GnssMeasurementRequest request,
+ IGnssMeasurementsListener listener, String packageName, @Nullable String attributionTag,
+ String listenerId) {
if (mGnssManagerService != null) {
mGnssManagerService.addGnssMeasurementsListener(request, listener, packageName,
- attributionTag);
+ attributionTag, listenerId);
}
}
@@ -937,10 +957,10 @@
@Override
public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
- String packageName, String attributionTag) {
+ String packageName, @Nullable String attributionTag, String listenerId) {
if (mGnssManagerService != null) {
mGnssManagerService.addGnssNavigationMessageListener(listener, packageName,
- attributionTag);
+ attributionTag, listenerId);
}
}
@@ -1268,13 +1288,13 @@
ipw.println("Historical Aggregate Location Provider Data:");
ipw.increaseIndent();
- ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats =
+ ArrayMap<String, ArrayMap<CallerIdentity, LocationEventLog.AggregateStats>> aggregateStats =
mEventLog.copyAggregateStats();
for (int i = 0; i < aggregateStats.size(); i++) {
ipw.print(aggregateStats.keyAt(i));
ipw.println(":");
ipw.increaseIndent();
- ArrayMap<String, LocationEventLog.AggregateStats> providerStats =
+ ArrayMap<CallerIdentity, LocationEventLog.AggregateStats> providerStats =
aggregateStats.valueAt(i);
for (int j = 0; j < providerStats.size(); j++) {
ipw.print(providerStats.keyAt(j));
@@ -1342,7 +1362,7 @@
if (provider != null && !provider.equals(manager.getName())) {
continue;
}
- if (identity.equalsIgnoringListenerId(manager.getIdentity())) {
+ if (identity.equals(manager.getIdentity())) {
return true;
}
}
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 6249a06..e1c011d 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -46,6 +46,7 @@
import com.android.server.location.ClientBrokerProto;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -101,6 +102,11 @@
private static final String TAG = "ContextHubClientBroker";
/**
+ * Internal only authorization value used when the auth state is unknown.
+ */
+ private static final int AUTHORIZATION_UNKNOWN = -1;
+
+ /**
* Message used by noteOp when this client receives a message from a nanoapp.
*/
private static final String RECEIVE_MSG_NOTE = "NanoappMessageDelivery ";
@@ -227,7 +233,7 @@
if (mMessageChannelNanoappIdMap.containsKey(state.getNanoAppId())) {
List<String> permissions = state.getNanoAppPermissions();
updateNanoAppAuthState(state.getNanoAppId(),
- hasPermissions(permissions), false /* gracePeriodExpired */);
+ permissions, false /* gracePeriodExpired */);
}
}
}
@@ -344,32 +350,13 @@
public int sendMessageToNanoApp(NanoAppMessage message) {
ContextHubServiceUtil.checkPermissions(mContext);
- int authState;
- synchronized (mMessageChannelNanoappIdMap) {
- // Default to the granted auth state. The true auth state will be checked async if it's
- // not denied.
- authState = mMessageChannelNanoappIdMap.getOrDefault(
- message.getNanoAppId(), AUTHORIZATION_GRANTED);
- if (authState == AUTHORIZATION_DENIED) {
- return ContextHubTransaction.RESULT_FAILED_PERMISSION_DENIED;
- }
- }
-
int result;
if (isRegistered()) {
- // Even though the auth state is currently not denied, query the nanoapp permissions
- // async and verify that the host app currently holds all the requisite permissions.
- // This can't be done synchronously due to the async query that needs to be performed to
- // obtain the nanoapp permissions.
- boolean initialNanoappMessage = false;
- synchronized (mMessageChannelNanoappIdMap) {
- if (mMessageChannelNanoappIdMap.get(message.getNanoAppId()) == null) {
- mMessageChannelNanoappIdMap.put(message.getNanoAppId(), AUTHORIZATION_GRANTED);
- initialNanoappMessage = true;
- }
- }
-
- if (initialNanoappMessage) {
+ int authState = mMessageChannelNanoappIdMap.getOrDefault(
+ message.getNanoAppId(), AUTHORIZATION_UNKNOWN);
+ if (authState == AUTHORIZATION_DENIED) {
+ return ContextHubTransaction.RESULT_FAILED_PERMISSION_DENIED;
+ } else if (authState == AUTHORIZATION_UNKNOWN) {
// Only check permissions the first time a nanoapp is queried since nanoapp
// permissions don't currently change at runtime. If the host permission changes
// later, that'll be checked by onOpChanged.
@@ -472,7 +459,8 @@
List<String> messagePermissions) {
long nanoAppId = message.getNanoAppId();
- int authState = mMessageChannelNanoappIdMap.getOrDefault(nanoAppId, AUTHORIZATION_GRANTED);
+ int authState = updateNanoAppAuthState(nanoAppId, nanoappPermissions,
+ false /* gracePeriodExpired */);
// If in the grace period, the host may not receive any messages containing permissions
// covered data.
@@ -482,7 +470,9 @@
return;
}
- if (authState == AUTHORIZATION_DENIED || !hasPermissions(nanoappPermissions)
+ // If in the grace period, don't check permissions state since it'll cause cleanup
+ // messages to be dropped.
+ if (authState == AUTHORIZATION_DENIED
|| !notePermissions(messagePermissions, RECEIVE_MSG_NOTE + nanoAppId)) {
Log.e(TAG, "Dropping message from " + Long.toHexString(nanoAppId) + ". " + mPackage
+ " doesn't have permission");
@@ -629,7 +619,8 @@
if (timer != null) {
updateNanoAppAuthState(
- nanoAppId, false /* hasPermissions */, true /* gracePeriodExpired */);
+ nanoAppId, Collections.emptyList() /* nanoappPermissions */,
+ true /* gracePeriodExpired */);
}
}
@@ -643,24 +634,43 @@
mTransactionManager.addTransaction(transaction);
}
- /**
- * Updates the latest authentication state for this client to be able to communicate with the
- * given nanoapp.
- */
- private void updateNanoAppAuthState(
- long nanoAppId, boolean hasPermissions, boolean gracePeriodExpired) {
- updateNanoAppAuthState(
- nanoAppId, hasPermissions, gracePeriodExpired, false /* forceDenied */);
+ private int updateNanoAppAuthState(
+ long nanoAppId, List<String> nanoappPermissions, boolean gracePeriodExpired) {
+ return updateNanoAppAuthState(
+ nanoAppId, nanoappPermissions, gracePeriodExpired, false /* forceDenied */);
}
- /* package */ void updateNanoAppAuthState(
- long nanoAppId, boolean hasPermissions, boolean gracePeriodExpired,
+ /**
+ * Updates the latest authenticatication state for the given nanoapp.
+ *
+ * @param nanoAppId the nanoapp that's auth state is being updated
+ * @param nanoappPermissions the Android permissions required to communicate with the nanoapp
+ * @param gracePeriodExpired indicates whether this invocation is a result of the grace period
+ * expiring
+ * @param forceDenied indicates that no matter what auth state is asssociated with this nanoapp
+ * it should transition to denied
+ * @return the latest auth state as of the completion of this method.
+ */
+ /* package */ int updateNanoAppAuthState(
+ long nanoAppId, List<String> nanoappPermissions, boolean gracePeriodExpired,
boolean forceDenied) {
int curAuthState;
int newAuthState;
synchronized (mMessageChannelNanoappIdMap) {
+ // Check permission granted state synchronously since this method can be invoked from
+ // multiple threads.
+ boolean hasPermissions = hasPermissions(nanoappPermissions);
+
curAuthState = mMessageChannelNanoappIdMap.getOrDefault(
- nanoAppId, AUTHORIZATION_GRANTED);
+ nanoAppId, AUTHORIZATION_UNKNOWN);
+ if (curAuthState == AUTHORIZATION_UNKNOWN) {
+ // If there's never been an auth check performed, start the state as granted so the
+ // appropriate state transitions occur below and clients don't receive a granted
+ // callback if they're determined to be in the granted state initially.
+ curAuthState = AUTHORIZATION_GRANTED;
+ mMessageChannelNanoappIdMap.put(nanoAppId, AUTHORIZATION_GRANTED);
+ }
+
newAuthState = curAuthState;
// The below logic ensures that only the following transitions are possible:
// GRANTED -> DENIED_GRACE_PERIOD only if permissions have been lost
@@ -701,6 +711,7 @@
// Don't send the callback in the synchronized block or it could end up in a deadlock.
sendAuthStateCallback(nanoAppId, newAuthState);
}
+ return newAuthState;
}
private void sendAuthStateCallback(long nanoAppId, int authState) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 1b8f0de..81c1e45 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -942,8 +942,8 @@
mClientManager.forEachClientOfHub(contextHubId, client -> {
if (client.getPackageName().equals(packageName)) {
client.updateNanoAppAuthState(
- nanoAppId, false /* hasPermissions */, false /* gracePeriodExpired */,
- true /* forceDenied */);
+ nanoAppId, Collections.emptyList() /* nanoappPermissions */,
+ false /* gracePeriodExpired */, true /* forceDenied */);
}
});
}
diff --git a/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java b/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java
index af3907e..dda502b 100644
--- a/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java
+++ b/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java
@@ -320,13 +320,15 @@
"(source: " + detectedCountry.getSource()
+ ", countryISO: " + detectedCountry.getCountryIso() + ")")
+ " isAirplaneModeOff()=" + isAirplaneModeOff()
+ + " isWifiOn()=" + isWifiOn()
+ " mListener=" + mListener
+ " isGeoCoderImplemnted()=" + isGeoCoderImplemented());
}
if (startLocationBasedDetection && (detectedCountry == null
|| detectedCountry.getSource() > Country.COUNTRY_SOURCE_LOCATION)
- && isAirplaneModeOff() && mListener != null && isGeoCoderImplemented()) {
+ && (isAirplaneModeOff() || isWifiOn()) && mListener != null
+ && isGeoCoderImplemented()) {
if (DEBUG) Slog.d(TAG, "run startLocationBasedDetector()");
// Start finding location when the source is less reliable than the
// location and the airplane mode is off (as geocoder will not
@@ -387,6 +389,11 @@
mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 0;
}
+ protected boolean isWifiOn() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.WIFI_ON, 0) != 0;
+ }
+
/**
* Notify the country change.
*/
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index dbfd0a5..29ce378 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -64,16 +64,17 @@
private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 10;
@GuardedBy("mAggregateStats")
- private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats;
+ private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
public LocationEventLog() {
super(getLogSize());
mAggregateStats = new ArrayMap<>(4);
}
- public ArrayMap<String, ArrayMap<String, AggregateStats>> copyAggregateStats() {
+ /** Copies out all aggregated stats. */
+ public ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copyAggregateStats() {
synchronized (mAggregateStats) {
- ArrayMap<String, ArrayMap<String, AggregateStats>> copy = new ArrayMap<>(
+ ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copy = new ArrayMap<>(
mAggregateStats);
for (int i = 0; i < copy.size(); i++) {
copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i)));
@@ -82,17 +83,18 @@
}
}
- private AggregateStats getAggregateStats(String provider, String packageName) {
+ private AggregateStats getAggregateStats(String provider, CallerIdentity identity) {
synchronized (mAggregateStats) {
- ArrayMap<String, AggregateStats> packageMap = mAggregateStats.get(provider);
+ ArrayMap<CallerIdentity, AggregateStats> packageMap = mAggregateStats.get(provider);
if (packageMap == null) {
packageMap = new ArrayMap<>(2);
mAggregateStats.put(provider, packageMap);
}
- AggregateStats stats = packageMap.get(packageName);
+ CallerIdentity stripped = identity.stripListenerId();
+ AggregateStats stats = packageMap.get(stripped);
if (stats == null) {
stats = new AggregateStats();
- packageMap.put(packageName, stats);
+ packageMap.put(stripped, stats);
}
return stats;
}
@@ -117,34 +119,33 @@
public void logProviderClientRegistered(String provider, CallerIdentity identity,
LocationRequest request) {
addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request);
- getAggregateStats(provider, identity.getPackageName())
- .markRequestAdded(request.getIntervalMillis());
+ getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis());
}
/** Logs a client unregistration for a location provider. */
public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity);
- getAggregateStats(provider, identity.getPackageName()).markRequestRemoved();
+ getAggregateStats(provider, identity).markRequestRemoved();
}
/** Logs a client for a location provider entering the active state. */
public void logProviderClientActive(String provider, CallerIdentity identity) {
- getAggregateStats(provider, identity.getPackageName()).markRequestActive();
+ getAggregateStats(provider, identity).markRequestActive();
}
/** Logs a client for a location provider leaving the active state. */
public void logProviderClientInactive(String provider, CallerIdentity identity) {
- getAggregateStats(provider, identity.getPackageName()).markRequestInactive();
+ getAggregateStats(provider, identity).markRequestInactive();
}
/** Logs a client for a location provider entering the foreground state. */
public void logProviderClientForeground(String provider, CallerIdentity identity) {
- getAggregateStats(provider, identity.getPackageName()).markRequestForeground();
+ getAggregateStats(provider, identity).markRequestForeground();
}
/** Logs a client for a location provider leaving the foreground state. */
public void logProviderClientBackground(String provider, CallerIdentity identity) {
- getAggregateStats(provider, identity.getPackageName()).markRequestBackground();
+ getAggregateStats(provider, identity).markRequestBackground();
}
/** Logs a change to the provider request for a location provider. */
@@ -165,7 +166,7 @@
if (Build.IS_DEBUGGABLE || D) {
addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
}
- getAggregateStats(provider, identity.getPackageName()).markLocationDelivered();
+ getAggregateStats(provider, identity).markLocationDelivered();
}
/** Logs that a provider has entered or exited stationary throttling. */
@@ -218,7 +219,7 @@
protected final String mProvider;
- protected ProviderEvent(long timeDelta, String provider) {
+ ProviderEvent(long timeDelta, String provider) {
super(timeDelta);
mProvider = provider;
}
@@ -234,7 +235,7 @@
private final int mUserId;
private final boolean mEnabled;
- protected ProviderEnabledEvent(long timeDelta, String provider, int userId,
+ ProviderEnabledEvent(long timeDelta, String provider, int userId,
boolean enabled) {
super(timeDelta, provider);
mUserId = userId;
@@ -252,7 +253,7 @@
private final boolean mMocked;
- protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
+ ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
super(timeDelta, provider);
mMocked = mocked;
}
@@ -273,7 +274,7 @@
private final CallerIdentity mIdentity;
@Nullable private final LocationRequest mLocationRequest;
- private ProviderRegisterEvent(long timeDelta, String provider, boolean registered,
+ ProviderRegisterEvent(long timeDelta, String provider, boolean registered,
CallerIdentity identity, @Nullable LocationRequest locationRequest) {
super(timeDelta, provider);
mRegistered = registered;
@@ -296,7 +297,7 @@
private final ProviderRequest mRequest;
- private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
+ ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
super(timeDelta, provider);
mRequest = request;
}
@@ -311,7 +312,7 @@
private final int mNumLocations;
- private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
+ ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
super(timeDelta, provider);
mNumLocations = numLocations;
}
@@ -327,7 +328,7 @@
private final int mNumLocations;
@Nullable private final CallerIdentity mIdentity;
- private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
+ ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
@Nullable CallerIdentity identity) {
super(timeDelta, provider);
mNumLocations = numLocations;
@@ -345,7 +346,7 @@
private final boolean mStationaryThrottled;
- private ProviderStationaryThrottledEvent(long timeDelta, String provider,
+ ProviderStationaryThrottledEvent(long timeDelta, String provider,
boolean stationaryThrottled) {
super(timeDelta, provider);
mStationaryThrottled = stationaryThrottled;
@@ -363,7 +364,7 @@
@LocationPowerSaveMode
private final int mLocationPowerSaveMode;
- private LocationPowerSaveModeEvent(long timeDelta,
+ LocationPowerSaveModeEvent(long timeDelta,
@LocationPowerSaveMode int locationPowerSaveMode) {
super(timeDelta);
mLocationPowerSaveMode = locationPowerSaveMode;
@@ -401,7 +402,7 @@
private final int mUserId;
private final boolean mEnabled;
- private LocationEnabledEvent(long timeDelta, int userId, boolean enabled) {
+ LocationEnabledEvent(long timeDelta, int userId, boolean enabled) {
super(timeDelta);
mUserId = userId;
mEnabled = enabled;
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index b6695c2..8312c63 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -60,7 +60,7 @@
private static final String ATTRIBUTION_ID = "GnssService";
- private final Context mContext;
+ final Context mContext;
private final GnssNative mGnssNative;
private final GnssLocationProvider mGnssLocationProvider;
@@ -154,10 +154,11 @@
* Registers listener for GNSS status changes.
*/
public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
- @Nullable String attributionTag) {
+ @Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
+ listenerId);
mGnssStatusProvider.addListener(identity, listener);
}
@@ -172,10 +173,11 @@
* Registers listener for GNSS NMEA messages.
*/
public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName,
- @Nullable String attributionTag) {
+ @Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
+ listenerId);
mGnssNmeaProvider.addListener(identity, listener);
}
@@ -191,12 +193,13 @@
*/
public void addGnssMeasurementsListener(GnssMeasurementRequest request,
IGnssMeasurementsListener listener, String packageName,
- @Nullable String attributionTag) {
+ @Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (request.isCorrelationVectorOutputsEnabled()) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
}
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
+ listenerId);
mGnssMeasurementsProvider.addListener(request, identity, listener);
}
@@ -223,10 +226,11 @@
* Adds a GNSS navigation message listener.
*/
public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
- String packageName, @Nullable String attributionTag) {
+ String packageName, @Nullable String attributionTag, String listenerId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
+ listenerId);
mGnssNavigationMessageProvider.addListener(identity, listener);
}
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index 4e0a0b8..08deb869 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -196,9 +196,12 @@
*/
protected AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity,
@Nullable ProviderProperties properties) {
+ Preconditions.checkArgument(identity == null || identity.getListenerId() == null);
mExecutor = executor;
mInternalState = new AtomicReference<>(new InternalState(null,
- State.EMPTY_STATE.withIdentity(identity).withProperties(properties)));
+ State.EMPTY_STATE
+ .withIdentity(identity)
+ .withProperties(properties)));
mController = new Controller();
}
@@ -273,6 +276,7 @@
* Call this method to report a change in provider packages.
*/
protected void setIdentity(@Nullable CallerIdentity identity) {
+ Preconditions.checkArgument(identity == null || identity.getListenerId() == null);
setState(state -> state.withIdentity(identity));
}
@@ -335,6 +339,8 @@
private boolean mStarted = false;
+ Controller() {}
+
@Override
public State setListener(@Nullable Listener listener) {
InternalState oldInternalState = mInternalState.getAndUpdate(
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 388b5a4..5a35d7f 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -304,6 +304,7 @@
LocationTransport transport, @PermissionLevel int permissionLevel) {
super(Objects.requireNonNull(request), identity, transport);
+ Preconditions.checkArgument(identity.getListenerId() != null);
Preconditions.checkArgument(permissionLevel > PERMISSION_NONE);
Preconditions.checkArgument(!request.getWorkSource().isEmpty());
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 30ea555..7e00fd6 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -194,7 +194,9 @@
}
public void reportMetric(boolean success) {
- FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, success);
+ // TODO(b/179105110) design error code; and report the true value for other fields.
+ FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, 0, 1, 1,
+ -1, 0);
}
public RebootEscrowEventLog getEventLog() {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 676f421..4b41466 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -29,6 +29,7 @@
import static android.os.Process.INVALID_UID;
import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessCapability;
import android.net.NetworkPolicyManager;
import android.os.UserHandle;
import android.util.Log;
@@ -97,13 +98,15 @@
}
}
- void uidStateChanged(int uid, int procState, long procStateSeq) {
+ void uidStateChanged(int uid, int procState, long procStateSeq,
+ @ProcessCapability int capability) {
synchronized (mLock) {
if (LOGV || uid == mDebugUid) {
Slog.v(TAG, uid + " state changed to "
- + ProcessList.makeProcStateString(procState) + " with seq=" + procStateSeq);
+ + ProcessList.makeProcStateString(procState) + ",seq=" + procStateSeq
+ + ",cap=" + ActivityManager.getCapabilitiesSummary(capability));
}
- mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq);
+ mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq, capability);
}
}
@@ -373,7 +376,8 @@
super(Data.class, capacity);
}
- public void uidStateChanged(int uid, int procState, long procStateSeq) {
+ public void uidStateChanged(int uid, int procState, long procStateSeq,
+ @ProcessCapability int capability) {
final Data data = getNextSlot();
if (data == null) return;
@@ -381,6 +385,7 @@
data.type = EVENT_UID_STATE_CHANGED;
data.ifield1 = uid;
data.ifield2 = procState;
+ data.ifield3 = capability;
data.lfield1 = procStateSeq;
data.timeStamp = System.currentTimeMillis();
}
@@ -546,8 +551,9 @@
case EVENT_NETWORK_BLOCKED:
return data.ifield1 + "-" + getBlockedReason(data.ifield2);
case EVENT_UID_STATE_CHANGED:
- return data.ifield1 + "-" + ProcessList.makeProcStateString(data.ifield2)
- + "-" + data.lfield1;
+ return data.ifield1 + ":" + ProcessList.makeProcStateString(data.ifield2)
+ + ":" + ActivityManager.getCapabilitiesSummary(data.ifield3)
+ + ":" + data.lfield1;
case EVENT_POLICIES_CHANGED:
return getPolicyChangedLog(data.ifield1, data.ifield2, data.ifield3);
case EVENT_METEREDNESS_CHANGED:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index aa7da54..ecf4438 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -131,6 +131,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessCapability;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -166,6 +167,7 @@
import android.net.NetworkIdentity;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
+import android.net.NetworkPolicyManager.UidState;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
@@ -557,7 +559,7 @@
/** Foreground at UID granularity. */
@GuardedBy("mUidRulesFirstLock")
- final SparseIntArray mUidState = new SparseIntArray();
+ final SparseArray<UidState> mUidState = new SparseArray<UidState>();
/** Map from network ID to last observed meteredness state */
@GuardedBy("mNetworkPoliciesSecondLock")
@@ -988,9 +990,12 @@
final private IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
- int capability) {
+ @ProcessCapability int capability) {
+ // TODO: Avoid creating a new UidStateCallbackInfo object every time
+ // we get a callback for an uid
mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED,
- uid, procState, procStateSeq).sendToTarget();
+ new UidStateCallbackInfo(uid, procState, procStateSeq, capability))
+ .sendToTarget();
}
@Override public void onUidGone(int uid, boolean disabled) {
@@ -1007,6 +1012,22 @@
}
};
+ private static final class UidStateCallbackInfo {
+ public final int uid;
+ public final int procState;
+ public final long procStateSeq;
+ @ProcessCapability
+ public final int capability;
+
+ UidStateCallbackInfo(int uid, int procState, long procStateSeq,
+ @ProcessCapability int capability) {
+ this.uid = uid;
+ this.procState = procState;
+ this.procStateSeq = procStateSeq;
+ this.capability = capability;
+ }
+ }
+
final private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -3718,14 +3739,12 @@
fout.print("UID=");
fout.print(uid);
- final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- fout.print(" state=");
- fout.print(state);
- if (state <= ActivityManager.PROCESS_STATE_TOP) {
- fout.print(" (fg)");
+ final UidState uidState = mUidState.get(uid);
+ if (uidState == null) {
+ fout.print(" state={null}");
} else {
- fout.print(state <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- ? " (fg svc)" : " (bg)");
+ fout.print(" state=");
+ fout.print(uidState.toString());
}
final int uidRules = mUidRules.get(uid, RULE_NONE);
@@ -3783,26 +3802,20 @@
@VisibleForTesting
boolean isUidForeground(int uid) {
synchronized (mUidRulesFirstLock) {
- return isUidStateForeground(
- mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY));
+ return isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.get(uid));
}
}
@GuardedBy("mUidRulesFirstLock")
private boolean isUidForegroundOnRestrictBackgroundUL(int uid) {
- final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- return isProcStateAllowedWhileOnRestrictBackground(procState);
+ final UidState uidState = mUidState.get(uid);
+ return isProcStateAllowedWhileOnRestrictBackground(uidState);
}
@GuardedBy("mUidRulesFirstLock")
private boolean isUidForegroundOnRestrictPowerUL(int uid) {
- final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- return isProcStateAllowedWhileIdleOrPowerSaveMode(procState);
- }
-
- private boolean isUidStateForeground(int state) {
- // only really in foreground when screen is also on
- return state <= NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE;
+ final UidState uidState = mUidState.get(uid);
+ return isProcStateAllowedWhileIdleOrPowerSaveMode(uidState);
}
/**
@@ -3811,16 +3824,18 @@
* {@link #updateRulesForPowerRestrictionsUL(int)}. Returns true if the state was updated.
*/
@GuardedBy("mUidRulesFirstLock")
- private boolean updateUidStateUL(int uid, int uidState) {
+ private boolean updateUidStateUL(int uid, int procState, @ProcessCapability int capability) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
try {
- final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- if (oldUidState != uidState) {
+ final UidState oldUidState = mUidState.get(uid);
+ if (oldUidState == null || oldUidState.procState != procState
+ || oldUidState.capability != capability) {
+ final UidState newUidState = new UidState(uid, procState, capability);
// state changed, push updated rules
- mUidState.put(uid, uidState);
- updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, uidState);
+ mUidState.put(uid, newUidState);
+ updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState);
if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
- != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) {
+ != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState)) {
updateRuleForAppIdleUL(uid);
if (mDeviceIdleMode) {
updateRuleForDeviceIdleUL(uid);
@@ -3842,11 +3857,10 @@
private boolean removeUidStateUL(int uid) {
final int index = mUidState.indexOfKey(uid);
if (index >= 0) {
- final int oldUidState = mUidState.valueAt(index);
+ final UidState oldUidState = mUidState.valueAt(index);
mUidState.removeAt(index);
- if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
- updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState,
- ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ if (oldUidState != null) {
+ updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, null);
if (mDeviceIdleMode) {
updateRuleForDeviceIdleUL(uid);
}
@@ -3873,8 +3887,8 @@
}
}
- private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid, int oldUidState,
- int newUidState) {
+ private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid,
+ @Nullable UidState oldUidState, @Nullable UidState newUidState) {
final boolean oldForeground =
isProcStateAllowedWhileOnRestrictBackground(oldUidState);
final boolean newForeground =
@@ -4979,11 +4993,14 @@
public boolean handleMessage(Message msg) {
switch (msg.what) {
case UID_MSG_STATE_CHANGED: {
- final int uid = msg.arg1;
- final int procState = msg.arg2;
- final long procStateSeq = (Long) msg.obj;
+ final UidStateCallbackInfo uidStateCallbackInfo =
+ (UidStateCallbackInfo) msg.obj;
+ final int uid = uidStateCallbackInfo.uid;
+ final int procState = uidStateCallbackInfo.procState;
+ final long procStateSeq = uidStateCallbackInfo.procStateSeq;
+ final int capability = uidStateCallbackInfo.capability;
- handleUidChanged(uid, procState, procStateSeq);
+ handleUidChanged(uid, procState, procStateSeq, capability);
return true;
}
case UID_MSG_GONE: {
@@ -4998,23 +5015,24 @@
}
};
- void handleUidChanged(int uid, int procState, long procStateSeq) {
+ void handleUidChanged(int uid, int procState, long procStateSeq,
+ @ProcessCapability int capability) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged");
try {
boolean updated;
synchronized (mUidRulesFirstLock) {
// We received a uid state change callback, add it to the history so that it
// will be useful for debugging.
- mLogger.uidStateChanged(uid, procState, procStateSeq);
+ mLogger.uidStateChanged(uid, procState, procStateSeq, capability);
// Now update the network policy rules as per the updated uid state.
- updated = updateUidStateUL(uid, procState);
+ updated = updateUidStateUL(uid, procState, capability);
// Updating the network rules is done, so notify AMS about this.
mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq);
}
// Do this without the lock held. handleUidChanged() and handleUidGone() are
// called from the handler, so there's no multi-threading issue.
if (updated) {
- updateNetworkStats(uid, isUidStateForeground(procState));
+ updateNetworkStats(uid, isProcStateAllowedWhileOnRestrictBackground(procState));
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -5349,6 +5367,13 @@
}
}
+ private static void collectKeys(SparseArray<UidState> source, SparseBooleanArray target) {
+ final int size = source.size();
+ for (int i = 0; i < size; i++) {
+ target.put(source.keyAt(i), true);
+ }
+ }
+
@Override
public void factoryReset(String subscriber) {
mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index f078242..4500bbc 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -39,6 +39,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.XmlUtils;
+import com.android.server.pm.PackageManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -465,6 +466,7 @@
return PendingIntent.getBroadcast(mContext,
REQUEST_CODE_REPOST,
new Intent(REPOST_ACTION)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
.setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build())
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
.putExtra(EXTRA_KEY, key)
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index d95a725..9c4c510 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -425,7 +425,7 @@
}
}
stream.end(token);
-
+ break;
case (int) Tombstone.SELINUX_LABEL:
selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 5d7c41c..1acbabd 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -69,6 +69,7 @@
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.Executor;
+import java.util.function.Function;
/**
* The entity responsible for filtering visibility between apps based on declarations in their
@@ -1354,14 +1355,13 @@
}
public void dumpQueries(
- PrintWriter pw, PackageManagerService pms, @Nullable Integer filteringAppId,
- DumpState dumpState,
- int[] users) {
+ PrintWriter pw, @Nullable Integer filteringAppId, DumpState dumpState, int[] users,
+ Function<Integer, String[]> getPackagesForUid) {
final SparseArray<String> cache = new SparseArray<>();
ToString<Integer> expandPackages = input -> {
String cachedValue = cache.get(input);
if (cachedValue == null) {
- final String[] packagesForUid = pms.getPackagesForUid(input);
+ final String[] packagesForUid = getPackagesForUid.apply(input);
if (packagesForUid == null) {
cachedValue = "[unknown app id " + input + "]";
} else {
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 2a1fc87..380cdb1 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -55,6 +55,9 @@
private int mOptions;
private boolean mTitlePrinted;
+ private boolean mFullPreferred;
+
+ private String mTargetPackageName;
private SharedUserSetting mSharedUser;
@@ -99,4 +102,20 @@
public void setSharedUser(SharedUserSetting user) {
mSharedUser = user;
}
+
+ public String getTargetPackageName() {
+ return mTargetPackageName;
+ }
+
+ public void setTargetPackageName(String packageName) {
+ mTargetPackageName = packageName;
+ }
+
+ public boolean isFullPreferred() {
+ return mFullPreferred;
+ }
+
+ public void setFullPreferred(boolean fullPreferred) {
+ mFullPreferred = fullPreferred;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b9e3e0f..0a443f3 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -780,7 +780,8 @@
return getOatDir(codePath).getAbsolutePath();
}
- static File getOatDir(File codePath) {
+ /** Returns the oat dir for the given code path */
+ public static File getOatDir(File codePath) {
return new File(codePath, OAT_DIR_NAME);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5cb9d8f..7bf3c5c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -781,7 +781,7 @@
@GuardedBy("mLock")
private File mInheritedFilesBase;
@GuardedBy("mLock")
- private boolean mVerityFound;
+ private boolean mVerityFoundForApks;
/**
* Both flags should be guarded with mLock whenever changes need to be in lockstep.
@@ -1010,9 +1010,14 @@
throw new IllegalArgumentException(
"DataLoader installation of APEX modules is not allowed.");
}
+
if (this.params.dataLoaderParams.getComponentName().getPackageName()
- == SYSTEM_DATA_LOADER_PACKAGE) {
- assertShellOrSystemCalling("System data loaders");
+ == SYSTEM_DATA_LOADER_PACKAGE && mContext.checkCallingOrSelfPermission(
+ Manifest.permission.USE_SYSTEM_DATA_LOADERS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need the "
+ + "com.android.permission.USE_SYSTEM_DATA_LOADERS permission "
+ + "to use system data loaders");
}
}
@@ -1202,8 +1207,13 @@
@GuardedBy("mLock")
private void computeProgressLocked(boolean forcePublish) {
- mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
- + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
+ if (!mCommitted) {
+ mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
+ + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
+ } else {
+ // For incremental installs, continue publishing the install progress during committing.
+ mProgress = mIncrementalProgress;
+ }
// Only publish when meaningful change
if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
@@ -1939,9 +1949,11 @@
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session destroyed");
}
- // Client staging is fully done at this point
- mClientProgress = 1f;
- computeProgressLocked(true);
+ if (!isIncrementalInstallation()) {
+ // For non-incremental installs, client staging is fully done at this point
+ mClientProgress = 1f;
+ computeProgressLocked(true);
+ }
// This ongoing commit should keep session active, even though client
// will probably close their end.
@@ -2717,8 +2729,8 @@
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Missing existing base package");
}
- // Default to require only if existing base has fs-verity.
- mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled()
+ // Default to require only if existing base apk has fs-verity.
+ mVerityFoundForApks = PackageManagerServiceUtils.isApkVerityEnabled()
&& params.mode == SessionParams.MODE_INHERIT_EXISTING
&& VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
@@ -3013,34 +3025,18 @@
}
@GuardedBy("mLock")
- private void maybeStageFsveritySignatureLocked(File origFile, File targetFile)
- throws PackageManagerException {
+ private void maybeStageFsveritySignatureLocked(File origFile, File targetFile,
+ boolean fsVerityRequired) throws PackageManagerException {
final File originalSignature = new File(
VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
- // Make sure .fsv_sig exists when it should, then resolve and stage it.
if (originalSignature.exists()) {
- // mVerityFound can only change from false to true here during the staging loop. Since
- // all or none of files should have .fsv_sig, this should only happen in the first time
- // (or never), otherwise bail out.
- if (!mVerityFound) {
- mVerityFound = true;
- if (mResolvedStagedFiles.size() > 1) {
- throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
- "Some file is missing fs-verity signature");
- }
- }
- } else {
- if (!mVerityFound) {
- return;
- }
+ final File stagedSignature = new File(
+ VerityUtils.getFsveritySignatureFilePath(targetFile.getPath()));
+ stageFileLocked(originalSignature, stagedSignature);
+ } else if (fsVerityRequired) {
throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
"Missing corresponding fs-verity signature to " + origFile);
}
-
- final File stagedSignature = new File(
- VerityUtils.getFsveritySignatureFilePath(targetFile.getPath()));
-
- stageFileLocked(originalSignature, stagedSignature);
}
@GuardedBy("mLock")
@@ -3059,7 +3055,11 @@
DexMetadataHelper.buildDexMetadataPathForApk(targetFile.getName()));
stageFileLocked(dexMetadataFile, targetDexMetadataFile);
- maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile);
+
+ // Also stage .dm.fsv_sig. .dm may be required to install with fs-verity signature on
+ // supported on older devices.
+ maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile,
+ VerityUtils.isFsVeritySupported() && DexMetadataHelper.isFsVerityRequired());
}
private void storeBytesToInstallationFile(final String localPath, final String absolutePath,
@@ -3121,13 +3121,45 @@
}
@GuardedBy("mLock")
+ private boolean isFsVerityRequiredForApk(File origFile, File targetFile)
+ throws PackageManagerException {
+ if (mVerityFoundForApks) {
+ return true;
+ }
+
+ // We haven't seen .fsv_sig for any APKs. Treat it as not required until we see one.
+ final File originalSignature = new File(
+ VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
+ if (!originalSignature.exists()) {
+ return false;
+ }
+ mVerityFoundForApks = true;
+
+ // When a signature is found, also check any previous staged APKs since they also need to
+ // have fs-verity signature consistently.
+ for (File file : mResolvedStagedFiles) {
+ if (!file.getName().endsWith(".apk")) {
+ continue;
+ }
+ // Ignore the current targeting file.
+ if (targetFile.getName().equals(file.getName())) {
+ continue;
+ }
+ throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
+ "Previously staged apk is missing fs-verity signature");
+ }
+ return true;
+ }
+
+ @GuardedBy("mLock")
private void resolveAndStageFileLocked(File origFile, File targetFile, String splitName)
throws PackageManagerException {
stageFileLocked(origFile, targetFile);
- // Stage fsverity signature if present.
- maybeStageFsveritySignatureLocked(origFile, targetFile);
- // Stage dex metadata (.dm) if present.
+ // Stage APK's fs-verity signature if present.
+ maybeStageFsveritySignatureLocked(origFile, targetFile,
+ isFsVerityRequiredForApk(origFile, targetFile));
+ // Stage dex metadata (.dm) and corresponding fs-verity signature if present.
maybeStageDexMetadataLocked(origFile, targetFile);
// Stage checksums (.digests) if present.
maybeStageDigestsLocked(origFile, targetFile, splitName);
@@ -3779,6 +3811,7 @@
public void onPackageLoadingProgressChanged(float progress) {
synchronized (mLock) {
mIncrementalProgress = progress;
+ computeProgressLocked(true);
}
}
});
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 14d15ac..16966d4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -316,7 +316,6 @@
import android.util.IntArray;
import android.util.Log;
import android.util.LogPrinter;
-import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.MathUtils;
import android.util.PackageUtils;
@@ -370,6 +369,7 @@
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.ArtUtils;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
@@ -401,6 +401,7 @@
import com.android.server.utils.Watchable;
import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedLongSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
import com.android.server.utils.Watcher;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -1402,10 +1403,10 @@
// Currently known shared libraries.
@Watched
- final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries =
- new WatchedArrayMap<>();
+ final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+ mSharedLibraries = new WatchedArrayMap<>();
@Watched
- final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>>
+ final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
mStaticLibsByDeclaringPackage = new WatchedArrayMap<>();
// Mapping from instrumentation class names to info about them.
@@ -1756,6 +1757,11 @@
public boolean filterAppAccess(String packageName, int callingUid, int userId) {
return mPmInternal.filterAppAccess(packageName, callingUid, userId);
}
+
+ @Override
+ public int[] getAllUserIds() {
+ return mUserManager.getUserIds();
+ }
}
/**
@@ -1786,8 +1792,8 @@
public final Settings settings;
public final SparseIntArray isolatedOwners;
public final WatchedArrayMap<String, AndroidPackage> packages;
- public final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> sharedLibs;
- public final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> staticLibs;
+ public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibs;
+ public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> staticLibs;
public final WatchedArrayMap<ComponentName, ParsedInstrumentation> instrumentation;
public final WatchedSparseBooleanArray webInstantAppsDisabled;
public final ComponentName resolveComponentName;
@@ -1989,6 +1995,7 @@
SigningDetails getSigningDetails(int uid);
boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId);
boolean filterAppAccess(String packageName, int callingUid, int userId);
+ void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
}
/**
@@ -2005,9 +2012,9 @@
private final WatchedArrayMap<String, AndroidPackage> mPackages;
private final WatchedArrayMap<ComponentName, ParsedInstrumentation>
mInstrumentation;
- private final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>>
+ private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
mStaticLibsByDeclaringPackage;
- private final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>>
+ private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
mSharedLibraries;
private final ComponentName mLocalResolveComponentName;
private final ActivityInfo mResolveActivity;
@@ -2032,6 +2039,9 @@
private final InstantAppResolverConnection mInstantAppResolverConnection;
private final DefaultAppProvider mDefaultAppProvider;
private final DomainVerificationManagerInternal mDomainVerificationManager;
+ private final PackageDexOptimizer mPackageDexOptimizer;
+ private final DexManager mDexManager;
+ private final CompilerStats mCompilerStats;
// PackageManagerService attributes that are primitives are referenced through the
// pms object directly. Primitives are the only attributes so referenced.
@@ -2078,6 +2088,9 @@
mInstantAppResolverConnection = args.service.mInstantAppResolverConnection;
mDefaultAppProvider = args.service.mDefaultAppProvider;
mDomainVerificationManager = args.service.mDomainVerificationManager;
+ mPackageDexOptimizer = args.service.mPackageDexOptimizer;
+ mDexManager = args.service.mDexManager;
+ mCompilerStats = args.service.mCompilerStats;
// Used to reference PMS attributes that are primitives and which are not
// updated under control of the PMS lock.
@@ -3546,7 +3559,7 @@
packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
// Is this a static library?
- LongSparseArray<SharedLibraryInfo> versionedLib =
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
mStaticLibsByDeclaringPackage.get(packageName);
if (versionedLib == null || versionedLib.size() <= 0) {
return packageName;
@@ -4388,6 +4401,143 @@
userId);
}
+ public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+ final String packageName = dumpState.getTargetPackageName();
+
+ switch (type) {
+ case DumpState.DUMP_VERSION:
+ {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Database versions:");
+ mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, " "));
+ break;
+ }
+
+ case DumpState.DUMP_PREFERRED_XML:
+ {
+ pw.flush();
+ FileOutputStream fout = new FileOutputStream(fd);
+ BufferedOutputStream str = new BufferedOutputStream(fout);
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ try {
+ serializer.setOutput(str, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ serializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ mSettings.writePreferredActivitiesLPr(serializer, 0,
+ dumpState.isFullPreferred());
+ serializer.endDocument();
+ serializer.flush();
+ } catch (IllegalArgumentException e) {
+ pw.println("Failed writing: " + e);
+ } catch (IllegalStateException e) {
+ pw.println("Failed writing: " + e);
+ } catch (IOException e) {
+ pw.println("Failed writing: " + e);
+ }
+ break;
+ }
+
+ case DumpState.DUMP_QUERIES:
+ {
+ final PackageSetting setting = mSettings.getPackageLPr(packageName);
+ Integer filteringAppId = setting == null ? null : setting.appId;
+ mAppsFilter.dumpQueries(
+ pw, filteringAppId, dumpState, mUserManager.getUserIds(),
+ this::getPackagesForUid);
+ break;
+ }
+
+ case DumpState.DUMP_DOMAIN_PREFERRED:
+ {
+ final android.util.IndentingPrintWriter writer =
+ new android.util.IndentingPrintWriter(pw);
+ if (dumpState.onTitlePrinted()) pw.println();
+
+ writer.println("Domain verification status:");
+ writer.increaseIndent();
+ try {
+ mDomainVerificationManager.printState(writer, packageName,
+ UserHandle.USER_ALL, mSettings::getPackageLPr);
+ } catch (PackageManager.NameNotFoundException e) {
+ pw.println("Failure printing domain verification information");
+ Slog.e(TAG, "Failure printing domain verification information", e);
+ }
+ writer.decreaseIndent();
+ break;
+ }
+
+ case DumpState.DUMP_DEXOPT:
+ {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println();
+ ipw.println("Dexopt state:");
+ ipw.increaseIndent();
+ Collection<PackageSetting> pkgSettings;
+ if (packageName != null) {
+ PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName);
+ if (targetPkgSetting != null) {
+ pkgSettings = Collections.singletonList(targetPkgSetting);
+ } else {
+ ipw.println("Unable to find package: " + packageName);
+ return;
+ }
+ } else {
+ pkgSettings = mSettings.getPackagesLocked().values();
+ }
+
+ for (PackageSetting pkgSetting : pkgSettings) {
+ final AndroidPackage pkg = pkgSetting.getPkg();
+ if (pkg == null) {
+ continue;
+ }
+ ipw.println("[" + pkgSetting.name + "]");
+ ipw.increaseIndent();
+ mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting,
+ mDexManager.getPackageUseInfoOrDefault(pkg.getPackageName()));
+ ipw.decreaseIndent();
+ }
+ break;
+ }
+
+ case DumpState.DUMP_COMPILER_STATS:
+ {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println();
+ ipw.println("Compiler stats:");
+ ipw.increaseIndent();
+ Collection<AndroidPackage> packages;
+ if (packageName != null) {
+ AndroidPackage targetPackage = mPackages.get(packageName);
+ if (targetPackage != null) {
+ packages = Collections.singletonList(targetPackage);
+ } else {
+ ipw.println("Unable to find package: " + packageName);
+ return;
+ }
+ } else {
+ packages = mPackages.values();
+ }
+
+ for (AndroidPackage pkg : packages) {
+ final String pkgName = pkg.getPackageName();
+ ipw.println("[" + pkgName + "]");
+ ipw.increaseIndent();
+
+ CompilerStats.PackageStats stats = mCompilerStats.getPackageStats(pkgName);
+ if (stats == null) {
+ ipw.println("(No recorded stats)");
+ } else {
+ stats.dump(ipw);
+ }
+ ipw.decreaseIndent();
+ }
+ break;
+ }
+ } // switch
+ }
}
/**
@@ -4701,6 +4851,10 @@
// and an image with the flag set false does not use snapshots.
private static final boolean SNAPSHOT_ENABLED = true;
+ // The per-instance snapshot disable/enable flag. This is generally set to false in
+ // test instances and set to SNAPSHOT_ENABLED in operational instances.
+ private final boolean mSnapshotEnabled;
+
/**
* Return the live computer.
*/
@@ -4713,7 +4867,7 @@
* The live computer will be returned if snapshots are disabled.
*/
private Computer snapshotComputer() {
- if (!SNAPSHOT_ENABLED) {
+ if (!mSnapshotEnabled) {
return mLiveComputer;
}
if (Thread.holdsLock(mLock)) {
@@ -6048,15 +6202,12 @@
mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
mResolveComponentName = testParams.resolveComponentName;
- // Create the computer as soon as the state objects have been installed. The
- // cached computer is the same as the live computer until the end of the
- // constructor, at which time the invalidation method updates it. The cache is
- // corked initially to ensure a cached computer is not built until the end of the
- // constructor.
- sSnapshotCorked = true;
+ // Disable snapshots in this instance of PackageManagerService, which is only used
+ // for testing. The instance still needs a live computer. The snapshot computer
+ // is set to null since it must never be used by this instance.
+ mSnapshotEnabled = false;
mLiveComputer = createLiveComputer();
- mSnapshotComputer = mLiveComputer;
- registerObserver();
+ mSnapshotComputer = null;
mPackages.putAll(testParams.packages);
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
@@ -6069,7 +6220,6 @@
mIncrementalVersion = testParams.incrementalVersion;
invalidatePackageInfoCache();
- sSnapshotCorked = false;
}
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
@@ -6215,6 +6365,7 @@
// constructor, at which time the invalidation method updates it. The cache is
// corked initially to ensure a cached computer is not built until the end of the
// constructor.
+ mSnapshotEnabled = SNAPSHOT_ENABLED;
sSnapshotCorked = true;
mLiveComputer = createLiveComputer();
mSnapshotComputer = mLiveComputer;
@@ -8038,7 +8189,7 @@
final int[] allUsers = mUserManager.getUserIds();
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
- final LongSparseArray<SharedLibraryInfo> versionedLib
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib
= mSharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
@@ -8303,7 +8454,8 @@
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
- LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.valueAt(i);
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+ mSharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
}
@@ -8372,7 +8524,8 @@
int libraryCount = mSharedLibraries.size();
for (int i = 0; i < libraryCount; i++) {
- LongSparseArray<SharedLibraryInfo> versionedLibrary = mSharedLibraries.valueAt(i);
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLibrary =
+ mSharedLibraries.valueAt(i);
if (versionedLibrary == null) {
continue;
}
@@ -8533,7 +8686,8 @@
Set<String> libs = null;
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
- LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.valueAt(i);
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+ mSharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
}
@@ -8959,6 +9113,10 @@
@Override
public List<String> getAllPackages() {
+ // Allow iorapd to call this method.
+ if (Binder.getCallingUid() != Process.IORAPD_UID) {
+ enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers");
+ }
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mLock) {
@@ -9837,7 +9995,7 @@
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
- return liveComputer().queryIntentActivitiesInternal(intent,
+ return snapshotComputer().queryIntentActivitiesInternal(intent,
resolvedType, flags, userId);
}
@@ -10324,7 +10482,7 @@
private @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
- return liveComputer().queryIntentServicesInternal(intent,
+ return snapshotComputer().queryIntentServicesInternal(intent,
resolvedType, flags, userId, callingUid,
includeInstantApps);
}
@@ -12198,10 +12356,10 @@
@Nullable
private static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
- Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) {
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
+ @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
if (newLibraries != null) {
- final LongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
SharedLibraryInfo info = null;
if (versionedLib != null) {
info = versionedLib.get(version);
@@ -12210,7 +12368,7 @@
return info;
}
}
- final LongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
if (versionedLib == null) {
return null;
}
@@ -12218,7 +12376,7 @@
}
private SharedLibraryInfo getLatestSharedLibraVersionLPr(AndroidPackage pkg) {
- LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
pkg.getStaticSharedLibName());
if (versionedLib == null) {
return null;
@@ -12523,8 +12681,8 @@
private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
Map<String, AndroidPackage> availablePackages,
- @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
+ @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
+ @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
throws PackageManagerException {
if (pkg == null) {
return null;
@@ -12621,8 +12779,8 @@
@NonNull String packageName, boolean required, int targetSdk,
@Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
@NonNull final Map<String, AndroidPackage> availablePackages,
- @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
+ @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
+ @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
throws PackageManagerException {
final int libCount = requestedLibraries.size();
for (int i = 0; i < libCount; i++) {
@@ -14043,7 +14201,7 @@
long minVersionCode = Long.MIN_VALUE;
long maxVersionCode = Long.MAX_VALUE;
- LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
pkg.getStaticSharedLibName());
if (versionedLib != null) {
final int versionCount = versionedLib.size();
@@ -14271,8 +14429,8 @@
}
private static boolean sharedLibExists(final String name, final long version,
- Map<String, LongSparseArray<SharedLibraryInfo>> librarySource) {
- LongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> librarySource) {
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
if (versionedLib != null && versionedLib.indexOfKey(version) >= 0) {
return true;
}
@@ -14282,9 +14440,9 @@
@GuardedBy("mLock")
private void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) {
final String name = libraryInfo.getName();
- LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
if (versionedLib == null) {
- versionedLib = new LongSparseArray<>();
+ versionedLib = new WatchedLongSparseArray<>();
mSharedLibraries.put(name, versionedLib);
}
final String declaringPackageName = libraryInfo.getDeclaringPackage().getPackageName();
@@ -14295,7 +14453,7 @@
}
private boolean removeSharedLibraryLPw(String name, long version) {
- LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
if (versionedLib == null) {
return false;
}
@@ -18290,7 +18448,7 @@
public final Map<String, ScanResult> scannedPackages;
public final Map<String, AndroidPackage> allPackages;
- public final Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource;
+ public final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource;
public final Map<String, InstallArgs> installArgs;
public final Map<String, PackageInstalledInfo> installResults;
public final Map<String, PrepareResult> preparedPackages;
@@ -18301,7 +18459,7 @@
Map<String, InstallArgs> installArgs,
Map<String, PackageInstalledInfo> installResults,
Map<String, PrepareResult> preparedPackages,
- Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
Map<String, VersionInfo> versionInfos,
Map<String, PackageSetting> lastStaticSharedLibSettings) {
@@ -18316,7 +18474,7 @@
}
private ReconcileRequest(Map<String, ScanResult> scannedPackages,
- Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
Map<String, VersionInfo> versionInfos,
Map<String, PackageSetting> lastStaticSharedLibSettings) {
@@ -18413,7 +18571,7 @@
combinedPackages.putAll(request.allPackages);
- final Map<String, LongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
+ final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
new ArrayMap<>();
for (String installPackageName : scannedPackages.keySet()) {
@@ -18637,7 +18795,7 @@
*/
private static List<SharedLibraryInfo> getAllowedSharedLibInfos(
ScanResult scanResult,
- Map<String, LongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
// Let's used the parsed package as scanResult.pkgSetting may be null
final ParsedPackage parsedPackage = scanResult.request.parsedPackage;
if (scanResult.staticSharedLibraryInfo == null
@@ -18707,7 +18865,7 @@
* added.
*/
private static boolean addSharedLibraryToPackageVersionMap(
- Map<String, LongSparseArray<SharedLibraryInfo>> target,
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
SharedLibraryInfo library) {
final String name = library.getName();
if (target.containsKey(name)) {
@@ -18719,7 +18877,7 @@
return false;
}
} else {
- target.put(name, new LongSparseArray<>());
+ target.put(name, new WatchedLongSparseArray<>());
}
target.get(name).put(library.getLongVersion(), library);
return true;
@@ -23541,10 +23699,8 @@
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
DumpState dumpState = new DumpState();
- boolean fullPreferred = false;
boolean checkin = false;
- String packageName = null;
ArraySet<String> permissionNames = null;
int opti = 0;
@@ -23613,7 +23769,7 @@
opti++;
// Is this a package name?
if ("android".equals(cmd) || cmd.contains(".")) {
- packageName = cmd;
+ dumpState.setTargetPackageName(cmd);
// When dumping a single package, we always dump all of its
// filter information since the amount of data will be reasonable.
dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
@@ -23694,7 +23850,7 @@
} else if ("preferred-xml".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PREFERRED_XML);
if (opti < args.length && "--full".equals(args[opti])) {
- fullPreferred = true;
+ dumpState.setFullPreferred(true);
opti++;
}
} else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) {
@@ -23747,257 +23903,208 @@
}
}
+ final String packageName = dumpState.getTargetPackageName();
if (checkin) {
pw.println("vers,1");
}
// reader
- synchronized (mLock) {
- if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
- if (!checkin) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Database versions:");
- mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, " "));
- }
+ if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
+ if (!checkin) {
+ dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
}
+ }
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES)
- && packageName == null) {
- if (dumpState.onTitlePrinted()) {
- pw.println();
- }
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
- ipw.println("Known Packages:");
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES)
+ && packageName == null) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ ipw.println("Known Packages:");
+ ipw.increaseIndent();
+ for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
+ final String knownPackage = mPmInternal.knownPackageToString(i);
+ ipw.print(knownPackage);
+ ipw.println(":");
+ final String[] pkgNames = mPmInternal.getKnownPackageNames(i,
+ UserHandle.USER_SYSTEM);
ipw.increaseIndent();
- for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
- final String knownPackage = mPmInternal.knownPackageToString(i);
- ipw.print(knownPackage);
- ipw.println(":");
- final String[] pkgNames = mPmInternal.getKnownPackageNames(i,
- UserHandle.USER_SYSTEM);
- ipw.increaseIndent();
- if (ArrayUtils.isEmpty(pkgNames)) {
- ipw.println("none");
- } else {
- for (String name : pkgNames) {
- ipw.println(name);
- }
+ if (ArrayUtils.isEmpty(pkgNames)) {
+ ipw.println("none");
+ } else {
+ for (String name : pkgNames) {
+ ipw.println(name);
}
- ipw.decreaseIndent();
}
ipw.decreaseIndent();
}
+ ipw.decreaseIndent();
+ }
- if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ final String requiredVerifierPackage = mRequiredVerifierPackage;
+ if (!checkin) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Verifiers:");
+ pw.print(" Required: ");
+ pw.print(requiredVerifierPackage);
+ pw.print(" (uid=");
+ pw.print(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
+ pw.println(")");
+ } else if (requiredVerifierPackage != null) {
+ pw.print("vrfy,"); pw.print(requiredVerifierPackage);
+ pw.print(",");
+ pw.println(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
+ }
+ }
+
+ if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) {
+ final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
+ final ComponentName verifierComponent = proxy.getComponentName();
+ if (verifierComponent != null) {
+ String verifierPackageName = verifierComponent.getPackageName();
if (!checkin) {
if (dumpState.onTitlePrinted())
pw.println();
- pw.println("Verifiers:");
- pw.print(" Required: ");
- pw.print(mRequiredVerifierPackage);
+ pw.println("Domain Verifier:");
+ pw.print(" Using: ");
+ pw.print(verifierPackageName);
pw.print(" (uid=");
- pw.print(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
pw.println(")");
- } else if (mRequiredVerifierPackage != null) {
- pw.print("vrfy,"); pw.print(mRequiredVerifierPackage);
+ } else if (verifierPackageName != null) {
+ pw.print("dv,"); pw.print(verifierPackageName);
pw.print(",");
- pw.println(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
}
+ } else {
+ pw.println();
+ pw.println("No Domain Verifier available!");
+ }
+ }
+
+ if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
+ // TODO: Move it to ComputerEngine once LongSparseArray<SharedLibraryInfo> is copied
+ // in snapshot.
+ synchronized (mLock) {
+ dumpSharedLibrariesLPr(pw, dumpState, checkin);
+ }
+ }
+
+ if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ if (!checkin) {
+ pw.println("Features:");
}
- if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) &&
- packageName == null) {
- DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
- ComponentName verifierComponent = proxy.getComponentName();
- if (verifierComponent != null) {
- String verifierPackageName = verifierComponent.getPackageName();
- if (!checkin) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Domain Verifier:");
- pw.print(" Using: ");
- pw.print(verifierPackageName);
- pw.print(" (uid=");
- pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
- pw.println(")");
- } else if (verifierPackageName != null) {
- pw.print("dv,"); pw.print(verifierPackageName);
+ synchronized (mAvailableFeatures) {
+ for (FeatureInfo feat : mAvailableFeatures.values()) {
+ if (checkin) {
+ pw.print("feat,");
+ pw.print(feat.name);
pw.print(",");
- pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
- }
- } else {
- pw.println();
- pw.println("No Domain Verifier available!");
- }
- }
-
- if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
- boolean printedHeader = false;
- final int numSharedLibraries = mSharedLibraries.size();
- for (int index = 0; index < numSharedLibraries; index++) {
- final String libName = mSharedLibraries.keyAt(index);
- LongSparseArray<SharedLibraryInfo> versionedLib
- = mSharedLibraries.get(libName);
- if (versionedLib == null) {
- continue;
- }
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
- if (!checkin) {
- if (!printedHeader) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Libraries:");
- printedHeader = true;
- }
- pw.print(" ");
- } else {
- pw.print("lib,");
- }
- pw.print(libraryInfo.getName());
- if (libraryInfo.isStatic()) {
- pw.print(" version=" + libraryInfo.getLongVersion());
- }
- if (!checkin) {
- pw.print(" -> ");
- }
- if (libraryInfo.getPath() != null) {
- if (libraryInfo.isNative()) {
- pw.print(" (so) ");
- } else {
- pw.print(" (jar) ");
- }
- pw.print(libraryInfo.getPath());
- } else {
- pw.print(" (apk) ");
- pw.print(libraryInfo.getPackageName());
+ pw.println(feat.version);
+ } else {
+ pw.print(" ");
+ pw.print(feat.name);
+ if (feat.version > 0) {
+ pw.print(" version=");
+ pw.print(feat.version);
}
pw.println();
}
}
}
+ }
- if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
- if (dumpState.onTitlePrinted())
- pw.println();
- if (!checkin) {
- pw.println("Features:");
- }
-
- synchronized (mAvailableFeatures) {
- for (FeatureInfo feat : mAvailableFeatures.values()) {
- if (checkin) {
- pw.print("feat,");
- pw.print(feat.name);
- pw.print(",");
- pw.println(feat.version);
- } else {
- pw.print(" ");
- pw.print(feat.name);
- if (feat.version > 0) {
- pw.print(" version=");
- pw.print(feat.version);
- }
- pw.println();
- }
- }
- }
- }
-
- if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+ // TODO: This cannot be moved to ComputerEngine since some variables with collections
+ // types in IntentResolver such as mTypeToFilter do not have a copy of `F[]`.
+ synchronized (mLock) {
mSettings.dumpPreferred(pw, dumpState, packageName);
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
- pw.flush();
- FileOutputStream fout = new FileOutputStream(fd);
- BufferedOutputStream str = new BufferedOutputStream(fout);
- TypedXmlSerializer serializer = Xml.newFastSerializer();
- try {
- serializer.setOutput(str, StandardCharsets.UTF_8.name());
- serializer.startDocument(null, true);
- serializer.setFeature(
- "http://xmlpull.org/v1/doc/features.html#indent-output", true);
- mSettings.writePreferredActivitiesLPr(serializer, 0, fullPreferred);
- serializer.endDocument();
- serializer.flush();
- } catch (IllegalArgumentException e) {
- pw.println("Failed writing: " + e);
- } catch (IllegalStateException e) {
- pw.println("Failed writing: " + e);
- } catch (IOException e) {
- pw.println("Failed writing: " + e);
- }
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
+ dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
- android.util.IndentingPrintWriter writer =
- new android.util.IndentingPrintWriter(pw);
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+ dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
+ }
- writer.println("Domain verification status:");
- writer.increaseIndent();
- try {
- mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL,
- mSettings::getPackageLPr);
- } catch (PackageManager.NameNotFoundException e) {
- pw.println("Failure printing domain verification information");
- Slog.e(TAG, "Failure printing domain verification information", e);
- }
- writer.decreaseIndent();
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+ mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
- mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState);
- }
-
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ synchronized (mLock) {
mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
}
+ }
- if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
+ if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
+ // This cannot be moved to ComputerEngine since some variables of the collections
+ // in PackageUserState such as suspendParams, disabledComponents and enabledComponents
+ // do not have a copy.
+ synchronized (mLock) {
mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin);
}
+ }
- if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
- final PackageSetting setting = mSettings.getPackageLPr(packageName);
- Integer filteringAppId = setting == null ? null : setting.appId;
- mAppsFilter.dumpQueries(
- pw, this, filteringAppId, dumpState,
- mUserManager.getUserIds());
- }
+ if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
+ dump(DumpState.DUMP_QUERIES, fd, pw, dumpState);
+ }
- if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+ if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+ // This cannot be moved to ComputerEngine since the set of packages in the
+ // SharedUserSetting do not have a copy.
+ synchronized (mLock) {
mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
}
+ }
- if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
- if (dumpState.onTitlePrinted()) pw.println();
- pw.println("Package Changes:");
+ if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ pw.println("Package Changes:");
+ synchronized (mLock) {
pw.print(" Sequence number="); pw.println(mChangedPackagesSequenceNumber);
final int K = mChangedPackages.size();
for (int i = 0; i < K; i++) {
@@ -24019,16 +24126,18 @@
}
}
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
- // XXX should handle packageName != null by dumping only install data that
- // the given package is involved with.
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
+ // XXX should handle packageName != null by dumping only install data that
+ // the given package is involved with.
+ if (dumpState.onTitlePrinted()) pw.println();
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
- ipw.println();
- ipw.println("Frozen packages:");
- ipw.increaseIndent();
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ ipw.println();
+ ipw.println("Frozen packages:");
+ ipw.increaseIndent();
+ synchronized (mLock) {
if (mFrozenPackages.size() == 0) {
ipw.println("(none)");
} else {
@@ -24036,16 +24145,18 @@
ipw.println(mFrozenPackages.valueAt(i));
}
}
- ipw.decreaseIndent();
}
+ ipw.decreaseIndent();
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
+ if (dumpState.onTitlePrinted()) pw.println();
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
- ipw.println();
- ipw.println("Loaded volumes:");
- ipw.increaseIndent();
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ ipw.println();
+ ipw.println("Loaded volumes:");
+ ipw.increaseIndent();
+ synchronized (mLoadedVolumes) {
if (mLoadedVolumes.size() == 0) {
ipw.println("(none)");
} else {
@@ -24053,36 +24164,39 @@
ipw.println(mLoadedVolumes.valueAt(i));
}
}
- ipw.decreaseIndent();
}
+ ipw.decreaseIndent();
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+ && packageName == null) {
+ synchronized (mLock) {
mComponentResolver.dumpServicePermissions(pw, dumpState);
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
- if (dumpState.onTitlePrinted()) pw.println();
- dumpDexoptStateLPr(pw, packageName);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
- if (dumpState.onTitlePrinted()) pw.println();
- dumpCompilerStatsLPr(pw, packageName);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ synchronized (mLock) {
mSettings.dumpReadMessagesLPr(pw, dumpState);
-
- pw.println();
- pw.println("Package warning messages:");
- dumpCriticalInfo(pw, null);
}
+ pw.println();
+ pw.println("Package warning messages:");
+ dumpCriticalInfo(pw, null);
+ }
- if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
- dumpCriticalInfo(pw, "msg,");
- }
+ if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
+ dumpCriticalInfo(pw, "msg,");
}
// PackageInstaller should be called outside of mPackages lock
@@ -24117,6 +24231,14 @@
}
}
+ /**
+ * Dump package manager states to the file according to a given dumping type of
+ * {@link DumpState}.
+ */
+ private void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+ snapshotComputer().dump(type, fd, pw, dumpState);
+ }
+
//TODO: b/111402650
private void disableSkuSpecificApps() {
String apkList[] = mContext.getResources().getStringArray(
@@ -24191,7 +24313,7 @@
final int count = mSharedLibraries.size();
for (int i = 0; i < count; i++) {
final String libName = mSharedLibraries.keyAt(i);
- LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName);
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName);
if (versionedLib == null) {
continue;
}
@@ -24215,69 +24337,50 @@
}
}
- @GuardedBy("mLock")
- @SuppressWarnings("resource")
- private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println();
- ipw.println("Dexopt state:");
- ipw.increaseIndent();
- Collection<PackageSetting> pkgSettings;
- if (packageName != null) {
- PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName);
- if (targetPkgSetting != null) {
- pkgSettings = Collections.singletonList(targetPkgSetting);
- } else {
- ipw.println("Unable to find package: " + packageName);
- return;
- }
- } else {
- pkgSettings = mSettings.getPackagesLocked().values();
- }
-
- for (PackageSetting pkgSetting : pkgSettings) {
- if (pkgSetting.pkg == null) {
+ private void dumpSharedLibrariesLPr(PrintWriter pw, DumpState dumpState, boolean checkin) {
+ boolean printedHeader = false;
+ final int numSharedLibraries = mSharedLibraries.size();
+ for (int index = 0; index < numSharedLibraries; index++) {
+ final String libName = mSharedLibraries.keyAt(index);
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName);
+ if (versionedLib == null) {
continue;
}
- ipw.println("[" + pkgSetting.name + "]");
- ipw.increaseIndent();
- mPackageDexOptimizer.dumpDexoptState(ipw, pkgSetting.pkg, pkgSetting,
- mDexManager.getPackageUseInfoOrDefault(pkgSetting.pkg.getPackageName()));
- ipw.decreaseIndent();
- }
- }
-
- @GuardedBy("mLock")
- @SuppressWarnings("resource")
- private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println();
- ipw.println("Compiler stats:");
- ipw.increaseIndent();
- Collection<AndroidPackage> packages;
- if (packageName != null) {
- AndroidPackage targetPackage = mPackages.get(packageName);
- if (targetPackage != null) {
- packages = Collections.singletonList(targetPackage);
- } else {
- ipw.println("Unable to find package: " + packageName);
- return;
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
+ if (!checkin) {
+ if (!printedHeader) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Libraries:");
+ printedHeader = true;
+ }
+ pw.print(" ");
+ } else {
+ pw.print("lib,");
+ }
+ pw.print(libraryInfo.getName());
+ if (libraryInfo.isStatic()) {
+ pw.print(" version=" + libraryInfo.getLongVersion());
+ }
+ if (!checkin) {
+ pw.print(" -> ");
+ }
+ if (libraryInfo.getPath() != null) {
+ if (libraryInfo.isNative()) {
+ pw.print(" (so) ");
+ } else {
+ pw.print(" (jar) ");
+ }
+ pw.print(libraryInfo.getPath());
+ } else {
+ pw.print(" (apk) ");
+ pw.print(libraryInfo.getPackageName());
+ }
+ pw.println();
}
- } else {
- packages = mPackages.values();
- }
-
- for (AndroidPackage pkg : packages) {
- ipw.println("[" + pkg.getPackageName() + "]");
- ipw.increaseIndent();
-
- CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.getPackageName());
- if (stats == null) {
- ipw.println("(No recorded stats)");
- } else {
- stats.dump(ipw);
- }
- ipw.decreaseIndent();
}
}
@@ -24439,7 +24542,9 @@
if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
sendResourcesChangedBroadcast(true, false, loaded, null);
- mLoadedVolumes.add(vol.getId());
+ synchronized (mLoadedVolumes) {
+ mLoadedVolumes.add(vol.getId());
+ }
}
private void unloadPrivatePackages(final VolumeInfo vol) {
@@ -24487,7 +24592,9 @@
if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
sendResourcesChangedBroadcast(false, false, unloaded, null);
- mLoadedVolumes.remove(vol.getId());
+ synchronized (mLoadedVolumes) {
+ mLoadedVolumes.remove(vol.getId());
+ }
// Try very hard to release any references to this path so we don't risk
// the system server being killed due to open FDs
@@ -27419,43 +27526,14 @@
}
}
- private String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) {
- if (!AndroidPackageUtils.canHaveOatDir(pkg,
- pkgSetting.getPkgState().isUpdatedSystemApp())) {
- return null;
- }
- File codePath = new File(pkg.getPath());
- if (codePath.isDirectory()) {
- return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
- }
- return null;
- }
-
void deleteOatArtifactsOfPackage(String packageName) {
- final String[] instructionSets;
- final List<String> codePaths;
- final String oatDir;
final AndroidPackage pkg;
final PackageSetting pkgSetting;
synchronized (mLock) {
pkg = mPackages.get(packageName);
pkgSetting = mSettings.getPackageLPr(packageName);
}
- instructionSets = getAppDexInstructionSets(
- AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
- codePaths = AndroidPackageUtils.getAllCodePaths(pkg);
- oatDir = getOatDir(pkg, pkgSetting);
-
- for (String codePath : codePaths) {
- for (String isa : instructionSets) {
- try {
- mInstaller.deleteOdex(codePath, isa, oatDir);
- } catch (InstallerException e) {
- Log.e(TAG, "Failed deleting oat files for " + codePath, e);
- }
- }
- }
+ mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting));
}
Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 2112247..ec7b451 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -40,7 +40,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.IntentFilterVerificationInfo;
-import android.content.pm.overlay.OverlayPaths;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageUserState;
@@ -72,6 +71,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.service.pm.PackageServiceDumpProto;
@@ -1028,6 +1028,9 @@
pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
}
pkgSetting.setPath(codePath);
+ if (IncrementalManager.isIncrementalPath(codePath.getAbsolutePath())) {
+ pkgSetting.incrementalStates = new IncrementalStates();
+ }
}
// If what we are scanning is a system (and possibly privileged) package,
// then make it so, regardless of whether it was previously installed only
@@ -4877,7 +4880,7 @@
}
}
- void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
+ void dumpPermissions(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
DumpState dumpState) {
LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames,
mPermissionDataProvider.getLegacyPermissions(),
diff --git a/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java b/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java
new file mode 100644
index 0000000..50bf916
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java
@@ -0,0 +1,58 @@
+/*
+ * 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.pm.dex;
+
+import java.util.List;
+
+/**
+ * Holds package information relevant to ART use cases.
+ */
+public class ArtPackageInfo {
+ private final String mPackageName;
+ private final List<String> mInstructionSets;
+ private final List<String> mCodePaths;
+ // TODO: This should be computed on the fly in PackageDexOptimizer / DexManager, but the
+ // logic is too complicated to do it in a single re-factoring.
+ private final String mOatDir;
+
+ public ArtPackageInfo(
+ String packageName,
+ List<String> instructionSets,
+ List<String> codePaths,
+ String oatDir) {
+ mPackageName = packageName;
+ mInstructionSets = instructionSets;
+ mCodePaths = codePaths;
+ mOatDir = oatDir;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public List<String> getInstructionSets() {
+ return mInstructionSets;
+ }
+
+ public List<String> getCodePaths() {
+ return mCodePaths;
+ }
+
+ public String getOatDir() {
+ return mOatDir;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/dex/ArtUtils.java b/services/core/java/com/android/server/pm/dex/ArtUtils.java
new file mode 100644
index 0000000..16d7a9a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtUtils.java
@@ -0,0 +1,64 @@
+/*
+ * 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.pm.dex;
+
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+
+import android.annotation.NonNull;
+
+import com.android.server.pm.PackageDexOptimizer;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+
+import java.io.File;
+import java.util.Arrays;
+
+/**
+ * Utility class to interface between PM and ART tooling (e.g. DexManager).
+ */
+public final class ArtUtils {
+ private ArtUtils() {
+ }
+
+ /**
+ * Create the ART-representation of package info.
+ */
+ public static ArtPackageInfo createArtPackageInfo(
+ AndroidPackage pkg, PackageSetting pkgSetting) {
+ return new ArtPackageInfo(
+ pkg.getPackageName(),
+ Arrays.asList(getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting))),
+ AndroidPackageUtils.getAllCodePaths(pkg),
+ getOatDir(pkg, pkgSetting));
+ }
+
+ private static String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) {
+ if (!AndroidPackageUtils.canHaveOatDir(pkg,
+ pkgSetting.getPkgState().isUpdatedSystemApp())) {
+ return null;
+ }
+ File codePath = new File(pkg.getPath());
+ if (codePath.isDirectory()) {
+ return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
+ }
+ return null;
+ }
+
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index cc6d80a..349561d 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -215,7 +215,7 @@
searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT;
if (primaryOrSplit && !isUsedByOtherApps
- && !PLATFORM_PACKAGE_NAME.equals(searchResult.mOwningPackageName)) {
+ && !isPlatformPackage(searchResult.mOwningPackageName)) {
// If the dex file is the primary apk (or a split) and not isUsedByOtherApps
// do not record it. This case does not bring any new usable information
// and can be safely skipped.
@@ -232,15 +232,24 @@
}
String classLoaderContext = mapping.getValue();
+
+ // Overwrite the class loader context for system server (instead of merging it).
+ // We expect system server jars to only change contexts in between OTAs and to
+ // otherwise be stable.
+ // Instead of implementing a complex clear-context logic post OTA, it is much
+ // simpler to always override the context for system server. This way, the context
+ // will always be up to date and we will avoid merging which could lead to the
+ // the context being marked as variable and thus making dexopt non-optimal.
+ boolean overwriteCLC = isPlatformPackage(searchResult.mOwningPackageName);
+
if (classLoaderContext != null
&& VMRuntime.isValidClassLoaderContext(classLoaderContext)) {
// Record dex file usage. If the current usage is a new pattern (e.g. new
// secondary, or UsedByOtherApps), record will return true and we trigger an
// async write to disk to make sure we don't loose the data in case of a reboot.
-
if (mPackageDexUsage.record(searchResult.mOwningPackageName,
dexPath, loaderUserId, loaderIsa, primaryOrSplit,
- loadingAppInfo.packageName, classLoaderContext)) {
+ loadingAppInfo.packageName, classLoaderContext, overwriteCLC)) {
mPackageDexUsage.maybeWriteAsync();
}
}
@@ -474,7 +483,7 @@
* because they don't need to be compiled)..
*/
public boolean dexoptSecondaryDex(DexoptOptions options) {
- if (PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) {
+ if (isPlatformPackage(options.getPackageName())) {
// We could easily redirect to #dexoptSystemServer in this case. But there should be
// no-one calling this method directly for system server.
// As such we prefer to abort in this case.
@@ -534,7 +543,7 @@
* <p>PackageDexOptimizer.DEX_OPT_PERFORMED if all dexopt operations succeeded.
*/
public int dexoptSystemServer(DexoptOptions options) {
- if (!PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) {
+ if (!isPlatformPackage(options.getPackageName())) {
Slog.wtf(TAG, "Non system server package used when trying to dexopt system server:"
+ options.getPackageName());
return PackageDexOptimizer.DEX_OPT_FAILED;
@@ -662,7 +671,7 @@
// Special handle system server files.
// We don't need an installd call because we have permissions to check if the file
// exists.
- if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+ if (isPlatformPackage(packageName)) {
if (!Files.exists(Paths.get(dexPath))) {
if (DEBUG) {
Slog.w(TAG, "A dex file previously loaded by System Server does not exist "
@@ -739,7 +748,8 @@
boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName,
dexPath, userId, isa, /*primaryOrSplit*/ false,
loadingPackage,
- PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT);
+ PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT,
+ /*overwriteCLC*/ false);
update |= newUpdate;
}
if (update) {
@@ -809,7 +819,7 @@
// Note: We don't have any way to detect which code paths are actually
// owned by system server. We can only assume that such paths are on
// system partitions.
- if (PLATFORM_PACKAGE_NAME.equals(loadingAppInfo.packageName)) {
+ if (isPlatformPackage(loadingAppInfo.packageName)) {
if (isSystemServerDexPathSupportedForOdex(dexPath)) {
// We record system server dex files as secondary dex files.
// The reason is that we only record the class loader context for secondary dex
@@ -842,6 +852,11 @@
return new DexSearchResult(null, DEX_SEARCH_NOT_FOUND);
}
+ /** Returns true if this is the platform package .*/
+ private static boolean isPlatformPackage(String packageName) {
+ return PLATFORM_PACKAGE_NAME.equals(packageName);
+ }
+
private static <K,V> V putIfAbsent(Map<K,V> map, K key, V newValue) {
V existingValue = map.putIfAbsent(key, newValue);
return existingValue == null ? newValue : existingValue;
@@ -1000,6 +1015,22 @@
return isBtmCritical;
}
+ /**
+ * Deletes all the optimizations files generated by ART.
+ * @param packageInfo the package information.
+ */
+ public void deleteOptimizedFiles(ArtPackageInfo packageInfo) {
+ for (String codePath : packageInfo.getCodePaths()) {
+ for (String isa : packageInfo.getInstructionSets()) {
+ try {
+ mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir());
+ } catch (InstallerException e) {
+ Log.e(TAG, "Failed deleting oat files for " + codePath, e);
+ }
+ }
+ }
+ }
+
public static class RegisterDexModuleResult {
public RegisterDexModuleResult() {
this(false, null);
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 10760f5..3d63b75 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -111,17 +111,18 @@
* @param dexPath the path of the dex files being loaded
* @param ownerUserId the user id which runs the code loading the dex files
* @param loaderIsa the ISA of the app loading the dex files
- * @param isUsedByOtherApps whether or not this dex file was not loaded by its owning package
* @param primaryOrSplit whether or not the dex file is a primary/split dex. True indicates
* the file is either primary or a split. False indicates the file is secondary dex.
* @param loadingPackageName the package performing the load. Recorded only if it is different
* than {@param owningPackageName}.
+ * @param overwriteCLC if true, the class loader context will be overwritten instead of being
+ * merged
* @return true if the dex load constitutes new information, or false if this information
* has been seen before.
*/
/* package */ boolean record(String owningPackageName, String dexPath, int ownerUserId,
String loaderIsa, boolean primaryOrSplit,
- String loadingPackageName, String classLoaderContext) {
+ String loadingPackageName, String classLoaderContext, boolean overwriteCLC) {
if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
throw new IllegalArgumentException("loaderIsa " + loaderIsa + " is unsupported");
}
@@ -193,7 +194,7 @@
}
// Merge the information into the existing data.
// Returns true if there was an update.
- return existingData.merge(newData) || updateLoadingPackages;
+ return existingData.merge(newData, overwriteCLC) || updateLoadingPackages;
}
}
}
@@ -809,14 +810,16 @@
mLoadingPackages = new HashSet<>(other.mLoadingPackages);
}
- private boolean merge(DexUseInfo dexUseInfo) {
+ private boolean merge(DexUseInfo dexUseInfo, boolean overwriteCLC) {
boolean oldIsUsedByOtherApps = mIsUsedByOtherApps;
mIsUsedByOtherApps = mIsUsedByOtherApps || dexUseInfo.mIsUsedByOtherApps;
boolean updateIsas = mLoaderIsas.addAll(dexUseInfo.mLoaderIsas);
boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
String oldClassLoaderContext = mClassLoaderContext;
- if (isUnsupportedContext(mClassLoaderContext)) {
+ if (overwriteCLC) {
+ mClassLoaderContext = dexUseInfo.mClassLoaderContext;
+ } else if (isUnsupportedContext(mClassLoaderContext)) {
mClassLoaderContext = dexUseInfo.mClassLoaderContext;
} else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
// We detected a context change.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 71e53d9..7a936ec 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -68,15 +68,10 @@
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
-import android.app.role.RoleManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.PermissionGroupInfoFlags;
import android.content.pm.PackageManager.PermissionInfoFlags;
@@ -86,7 +81,6 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
-import android.content.pm.UserInfo;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.permission.SplitPermissionInfoParcelable;
@@ -401,105 +395,6 @@
new PermissionManagerServiceInternalImpl();
LocalServices.addService(PermissionManagerServiceInternal.class, localService);
LocalServices.addService(PermissionManagerInternal.class, localService);
-
- context.getMainThreadHandler().post(() -> context.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
- return;
- }
-
- try {
- fixBgMicCamera(context);
- } catch (Throwable t) {
- // Don't crash the system if this fails for any reason. Any intermediate state
- // this can leave the permissions in is okay and in the worst case the state is
- // the same as before the user rebooted.
- Log.e(LOG_TAG, "Unable to fix background permissions", t);
- }
- }
-
-
- private void fixBgMicCamera(Context context) {
- PackageManager pm = context.getPackageManager();
- for (UserInfo userInfo : context.getSystemService(UserManager.class).getUsers()) {
- UserHandle user = userInfo.getUserHandle();
- List<String> assistants = context.getSystemService(RoleManager.class)
- .getRoleHoldersAsUser(RoleManager.ROLE_ASSISTANT, user);
- List<PackageInfo> packages =
- pm.getInstalledPackagesAsUser(PackageManager.MATCH_SYSTEM_ONLY
- | PackageManager.GET_PERMISSIONS, user.getIdentifier());
- for (PackageInfo packageInfo : packages) {
- String[] requestedPermissions = packageInfo.requestedPermissions;
- if (requestedPermissions == null) {
- continue;
- }
- for (String permName : requestedPermissions) {
- String pkg = packageInfo.packageName;
- switch (permName) {
- case Manifest.permission.BACKGROUND_CAMERA:
- removeFromAllowlistsAndRevoke(pm, pkg, permName, user);
- break;
- case Manifest.permission.RECORD_BACKGROUND_AUDIO:
- if (assistants.contains(pkg)) {
- removeFromAllowlistsAndRevokeForAssistant(pm, pkg, permName,
- user);
- } else {
- removeFromAllowlistsAndRevoke(pm, pkg, permName, user);
- }
- break;
- }
- }
- }
- }
- }
-
- private void removeFromAllowlistsAndRevoke(PackageManager pm, String pkg,
- String permName, UserHandle user) {
- if ((pm.getPermissionFlags(permName, pkg, user)
- & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0) {
- Slog.i(LOG_TAG, "removing " + pkg + " " + permName + " from all allowlists");
- pm.removeWhitelistedRestrictedPermission(pkg, permName,
- FLAG_PERMISSION_WHITELIST_UPGRADE);
- pm.removeWhitelistedRestrictedPermission(pkg, permName,
- FLAG_PERMISSION_WHITELIST_SYSTEM);
- pm.removeWhitelistedRestrictedPermission(pkg, permName,
- FLAG_PERMISSION_WHITELIST_INSTALLER);
- pm.removeWhitelistedRestrictedPermission(pkg, permName,
- FLAG_PERMISSION_ALLOWLIST_ROLE);
- }
- if (pm.checkPermission(permName, pkg) == PackageManager.PERMISSION_GRANTED) {
- Slog.i(LOG_TAG, "revoking " + pkg + " " + permName);
- pm.revokeRuntimePermission(pkg, permName, user);
- }
- }
-
- private void removeFromAllowlistsAndRevokeForAssistant(PackageManager pm, String pkg,
- String permName, UserHandle user) {
- int anyNonRoleExempt =
- FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
- | FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
- | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
-
- if ((pm.getPermissionFlags(permName, pkg, user) & anyNonRoleExempt) != 0) {
- Slog.i(LOG_TAG, "removing " + pkg + " " + permName
- + " from all allowlists except role");
- pm.removeWhitelistedRestrictedPermission(pkg, permName,
- FLAG_PERMISSION_WHITELIST_UPGRADE);
- pm.removeWhitelistedRestrictedPermission(pkg, permName,
- FLAG_PERMISSION_WHITELIST_SYSTEM);
- pm.removeWhitelistedRestrictedPermission(pkg, permName,
- FLAG_PERMISSION_WHITELIST_INSTALLER);
- }
- if ((pm.getPermissionFlags(permName, pkg, user)
- & FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT) == 0) {
- Slog.i(LOG_TAG, "adding " + pkg + " " + permName
- + " to role allowlist");
- pm.addWhitelistedRestrictedPermission(pkg, permName,
- FLAG_PERMISSION_ALLOWLIST_ROLE);
- }
- }
- }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)));
}
@Override
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 5d4370a..b6ea901 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -358,5 +358,8 @@
@Nullable
AndroidPackage getPackageLocked(@NonNull String pkgName);
+
+ @UserIdInt
+ int[] getAllUserIds();
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index e5ed774..8e5aead 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -423,12 +423,8 @@
for (int pkgStateIndex = 0; pkgStateIndex < pkgStateSize; pkgStateIndex++) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex);
if (userId == UserHandle.USER_ALL) {
- SparseArray<DomainVerificationUserState> userStates =
- pkgState.getUserSelectionStates();
- int userStatesSize = userStates.size();
- for (int userStateIndex = 0; userStateIndex < userStatesSize;
- userStateIndex++) {
- userStates.valueAt(userStateIndex)
+ for (int aUserId : mConnection.getAllUserIds()) {
+ pkgState.getOrCreateUserSelectionState(aUserId)
.setLinkHandlingAllowed(allowed);
}
} else {
@@ -436,7 +432,6 @@
.setLinkHandlingAllowed(allowed);
}
}
-
}
} else {
synchronized (mLock) {
@@ -500,28 +495,33 @@
@Override
public void setDomainVerificationUserSelectionInternal(@UserIdInt int userId,
- @Nullable String packageName, boolean enabled, @NonNull ArraySet<String> domains)
+ @Nullable String packageName, boolean enabled, @Nullable ArraySet<String> domains)
throws NameNotFoundException {
mEnforcer.assertInternal(mConnection.getCallingUid());
+
if (packageName == null) {
synchronized (mLock) {
Set<String> validDomains = new ArraySet<>();
-
int size = mAttachedPkgStates.size();
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
String pkgName = pkgState.getPackageName();
PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
- if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
+ if (pkg == null) {
continue;
}
- validDomains.clear();
- validDomains.addAll(domains);
+ if (domains == null) {
+ validDomains = mCollector.collectAllWebDomains(pkg);
+ } else {
+ validDomains.clear();
+ validDomains.addAll(domains);
+ }
setDomainVerificationUserSelectionInternal(userId, pkgState,
- pkgSetting.getPkg(), enabled, validDomains);
+ pkg, enabled, validDomains);
}
}
} else {
@@ -532,12 +532,16 @@
}
PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName);
- if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
+ if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
+ Set<String> validDomains =
+ domains == null ? mCollector.collectAllWebDomains(pkg) : domains;
+
setDomainVerificationUserSelectionInternal(userId, pkgState, pkgSetting.getPkg(),
- enabled, domains);
+ enabled, validDomains);
}
}
@@ -549,12 +553,10 @@
boolean enabled, Set<String> domains) {
domains.retainAll(mCollector.collectAllWebDomains(pkg));
- SparseArray<DomainVerificationUserState> userStates =
- pkgState.getUserSelectionStates();
if (userId == UserHandle.USER_ALL) {
- int size = userStates.size();
- for (int index = 0; index < size; index++) {
- DomainVerificationUserState userState = userStates.valueAt(index);
+ for (int aUserId : mConnection.getAllUserIds()) {
+ DomainVerificationUserState userState =
+ pkgState.getOrCreateUserSelectionState(aUserId);
if (enabled) {
userState.addHosts(domains);
} else {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
index 7f9e75a..d083d11 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -250,6 +250,10 @@
return false;
}
+ if (domains.size() == 1 && domains.contains("all")) {
+ domains = null;
+ }
+
try {
mCallback.setDomainVerificationUserSelectionInternal(userId,
packageName, enabled, domains);
@@ -446,10 +450,10 @@
* @param packageName the package whose state to change, or all packages if non is
* specified
* @param enabled whether the domain is now approved by the user
- * @param domains the set of domains to change
+ * @param domains the set of domains to change, or null to affect all domains
*/
void setDomainVerificationUserSelectionInternal(@UserIdInt int userId,
- @Nullable String packageName, boolean enabled, @NonNull ArraySet<String> domains)
+ @Nullable String packageName, boolean enabled, @Nullable ArraySet<String> domains)
throws PackageManager.NameNotFoundException;
/**
diff --git a/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
new file mode 100644
index 0000000..2fcd178
--- /dev/null
+++ b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
@@ -0,0 +1,280 @@
+/*
+ * 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.power;
+
+import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
+import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
+
+import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_ADDED;
+import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_CHANGED;
+import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.PowerManagerInternal;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.display.DisplayGroup;
+
+/**
+ * Responsible for creating {@link DisplayPowerRequest}s and associating them with
+ * {@link com.android.server.display.DisplayGroup}s.
+ *
+ * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest}
+ * which is used to request power state changes to every display in the group.
+ */
+public class DisplayGroupPowerStateMapper {
+
+ private static final String TAG = "DisplayPowerRequestMapper";
+
+ /** Lock obtained from {@link PowerManagerService}. */
+ private final Object mLock;
+
+ /** Listener to inform of changes to display groups. */
+ private final DisplayGroupPowerChangeListener mListener;
+
+ /** A mapping from DisplayGroup Id to DisplayGroup information. */
+ @GuardedBy("mLock")
+ private final SparseArray<DisplayGroupInfo> mDisplayGroupInfos = new SparseArray<>();
+
+ /** A cached array of DisplayGroup Ids. */
+ @GuardedBy("mLock")
+ private int[] mDisplayGroupIds;
+
+ private final DisplayManagerInternal.DisplayGroupListener mDisplayGroupListener =
+ new DisplayManagerInternal.DisplayGroupListener() {
+ @Override
+ public void onDisplayGroupAdded(int groupId) {
+ synchronized (mLock) {
+ if (mDisplayGroupInfos.contains(groupId)) {
+ Slog.e(TAG, "Tried to add already existing group:" + groupId);
+ return;
+ }
+ // For now, only the default group supports sandman (dream/AOD).
+ final boolean supportsSandman = groupId == Display.DEFAULT_DISPLAY_GROUP;
+ final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo(
+ new DisplayPowerRequest(),
+ getGlobalWakefulnessLocked(),
+ /* ready= */ false,
+ supportsSandman);
+ mDisplayGroupInfos.append(groupId, displayGroupInfo);
+ mDisplayGroupIds = ArrayUtils.appendInt(mDisplayGroupIds, groupId);
+ mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_ADDED, groupId);
+ }
+ }
+
+ @Override
+ public void onDisplayGroupRemoved(int groupId) {
+ synchronized (mLock) {
+ if (!mDisplayGroupInfos.contains(groupId)) {
+ Slog.e(TAG, "Tried to remove non-existent group:" + groupId);
+ return;
+ }
+ mDisplayGroupInfos.delete(groupId);
+ mDisplayGroupIds = ArrayUtils.removeInt(mDisplayGroupIds, groupId);
+ mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_REMOVED, groupId);
+ }
+ }
+
+ @Override
+ public void onDisplayGroupChanged(int groupId) {
+ synchronized (mLock) {
+ mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_CHANGED, groupId);
+ }
+ }
+ };
+
+ DisplayGroupPowerStateMapper(Object lock, DisplayManagerInternal displayManagerInternal,
+ DisplayGroupPowerChangeListener listener) {
+ mLock = lock;
+ mListener = listener;
+ displayManagerInternal.registerDisplayGroupListener(mDisplayGroupListener);
+
+ final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo(
+ new DisplayPowerRequest(), WAKEFULNESS_AWAKE, /* ready= */
+ false, /* supportsSandman= */ true);
+ mDisplayGroupInfos.append(Display.DEFAULT_DISPLAY_GROUP, displayGroupInfo);
+ mDisplayGroupIds = new int[]{Display.DEFAULT_DISPLAY_GROUP};
+ }
+
+ DisplayPowerRequest getPowerRequestLocked(int groupId) {
+ return mDisplayGroupInfos.get(groupId).displayPowerRequest;
+ }
+
+ int[] getDisplayGroupIdsLocked() {
+ return mDisplayGroupIds;
+ }
+
+ int getWakefulnessLocked(int groupId) {
+ return mDisplayGroupInfos.get(groupId).wakefulness;
+ }
+
+ void setLastPowerOnTimeLocked(int groupId, long eventTime) {
+ mDisplayGroupInfos.get(groupId).lastPowerOnTime = eventTime;
+ }
+
+ long getLastPowerOnTimeLocked(int groupId) {
+ return mDisplayGroupInfos.get(groupId).lastPowerOnTime;
+ }
+
+ /**
+ * Returns the amalgamated wakefulness of all {@link DisplayGroup DisplayGroups}.
+ *
+ * <p>This will be the highest wakeful state of all {@link DisplayGroup DisplayGroups}; ordered
+ * from highest to lowest:
+ * <ol>
+ * <li>{@link PowerManagerInternal#WAKEFULNESS_AWAKE}
+ * <li>{@link PowerManagerInternal#WAKEFULNESS_DREAMING}
+ * <li>{@link PowerManagerInternal#WAKEFULNESS_DOZING}
+ * <li>{@link PowerManagerInternal#WAKEFULNESS_ASLEEP}
+ * </ol>
+ */
+ int getGlobalWakefulnessLocked() {
+ final int size = mDisplayGroupInfos.size();
+ int deviceWakefulness = WAKEFULNESS_ASLEEP;
+ for (int i = 0; i < size; i++) {
+ final int wakefulness = mDisplayGroupInfos.valueAt(i).wakefulness;
+ if (wakefulness == WAKEFULNESS_AWAKE) {
+ return WAKEFULNESS_AWAKE;
+ } else if (wakefulness == WAKEFULNESS_DREAMING
+ && (deviceWakefulness == WAKEFULNESS_ASLEEP
+ || deviceWakefulness == WAKEFULNESS_DOZING)) {
+ deviceWakefulness = WAKEFULNESS_DREAMING;
+ } else if (wakefulness == WAKEFULNESS_DOZING
+ && deviceWakefulness == WAKEFULNESS_ASLEEP) {
+ deviceWakefulness = WAKEFULNESS_DOZING;
+ }
+ }
+
+ return deviceWakefulness;
+ }
+
+ /**
+ * Sets the {@code wakefulness} value for the {@link DisplayGroup} specified by the provided
+ * {@code groupId}.
+ *
+ * @return {@code true} if the wakefulness value was changed; {@code false} otherwise.
+ */
+ boolean setWakefulnessLocked(int groupId, int wakefulness) {
+ final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId);
+ if (displayGroupInfo.wakefulness != wakefulness) {
+ displayGroupInfo.wakefulness = wakefulness;
+ return true;
+ }
+
+ return false;
+ }
+
+ boolean isSandmanSummoned(int groupId) {
+ return mDisplayGroupInfos.get(groupId).sandmanSummoned;
+ }
+
+ boolean isSandmanSupported(int groupId) {
+ return mDisplayGroupInfos.get(groupId).supportsSandman;
+ }
+
+ /**
+ * Sets whether or not the sandman is summoned for the given {@code groupId}.
+ *
+ * @param groupId Signifies the DisplayGroup for which to summon or unsummon the
+ * sandman.
+ * @param sandmanSummoned {@code true} to summon the sandman; {@code false} to unsummon.
+ */
+ void setSandmanSummoned(int groupId, boolean sandmanSummoned) {
+ final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId);
+ displayGroupInfo.sandmanSummoned = displayGroupInfo.supportsSandman && sandmanSummoned;
+ }
+
+ /**
+ * Returns {@code true} if every display in the specified group has its requested state matching
+ * its actual state.
+ *
+ * @param groupId The identifier for the display group to check for readiness.
+ */
+ boolean isReady(int groupId) {
+ return mDisplayGroupInfos.get(groupId).ready;
+ }
+
+ /** Returns {@code true} if every display has its requested state matching its actual state. */
+ boolean areAllDisplaysReadyLocked() {
+ final int size = mDisplayGroupInfos.size();
+ for (int i = 0; i < size; i++) {
+ if (!mDisplayGroupInfos.valueAt(i).ready) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Sets whether the displays specified by the provided {@code groupId} are all ready.
+ *
+ * <p>A display is ready if its reported
+ * {@link DisplayManagerInternal.DisplayPowerCallbacks#onStateChanged() actual state} matches
+ * its {@link DisplayManagerInternal#requestPowerState requested state}.
+ *
+ * @param groupId The identifier for the display group.
+ * @param ready {@code true} if every display in the group is ready; otherwise {@code false}.
+ * @return {@code true} if the ready state changed; otherwise {@code false}.
+ */
+ boolean setDisplayGroupReadyLocked(int groupId, boolean ready) {
+ final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId);
+ if (displayGroupInfo.ready != ready) {
+ displayGroupInfo.ready = ready;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Interface through which an interested party may be informed of {@link DisplayGroup} events.
+ */
+ interface DisplayGroupPowerChangeListener {
+ int DISPLAY_GROUP_ADDED = 0;
+ int DISPLAY_GROUP_REMOVED = 1;
+ int DISPLAY_GROUP_CHANGED = 2;
+
+ void onDisplayGroupEventLocked(int event, int groupId);
+ }
+
+ private static final class DisplayGroupInfo {
+ public final DisplayPowerRequest displayPowerRequest;
+ public int wakefulness;
+ public boolean ready;
+ public long lastPowerOnTime;
+ public boolean sandmanSummoned;
+
+ /** {@code true} if this DisplayGroup supports dreaming; otherwise {@code false}. */
+ public boolean supportsSandman;
+
+ DisplayGroupInfo(DisplayPowerRequest displayPowerRequest, int wakefulness, boolean ready,
+ boolean supportsSandman) {
+ this.displayPowerRequest = displayPowerRequest;
+ this.wakefulness = wakefulness;
+ this.ready = ready;
+ this.supportsSandman = supportsSandman;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
deleted file mode 100644
index 2fc3e40..0000000
--- a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power;
-
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
-import android.os.Handler;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.Display;
-
-import com.android.internal.annotations.GuardedBy;
-
-/**
- * Responsible for creating {@link DisplayPowerRequest}s and associating them with
- * {@link com.android.server.display.DisplayGroup}s.
- *
- * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest}
- * which is used to request power state changes to every display in the group.
- */
-class DisplayPowerRequestMapper {
-
- private final Object mLock = new Object();
-
- /** A mapping from LogicalDisplay Id to DisplayGroup Id. */
- @GuardedBy("mLock")
- private final SparseIntArray mDisplayGroupIds = new SparseIntArray();
-
- /** A mapping from DisplayGroup Id to DisplayPowerRequest. */
- @GuardedBy("mLock")
- private final SparseArray<DisplayPowerRequest> mDisplayPowerRequests = new SparseArray<>();
-
- private final DisplayManagerInternal mDisplayManagerInternal;
-
- private final DisplayManager.DisplayListener mDisplayListener =
- new DisplayManager.DisplayListener() {
-
- @Override
- public void onDisplayAdded(int displayId) {
- synchronized (mLock) {
- if (mDisplayGroupIds.indexOfKey(displayId) >= 0) {
- return;
- }
- final int displayGroupId = mDisplayManagerInternal.getDisplayGroupId(
- displayId);
- if (!mDisplayPowerRequests.contains(displayGroupId)) {
- // A new DisplayGroup was created; create a new DisplayPowerRequest.
- mDisplayPowerRequests.append(displayGroupId, new DisplayPowerRequest());
- }
- mDisplayGroupIds.append(displayId, displayGroupId);
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- synchronized (mLock) {
- final int index = mDisplayGroupIds.indexOfKey(displayId);
- if (index < 0) {
- return;
- }
- final int displayGroupId = mDisplayGroupIds.valueAt(index);
- mDisplayGroupIds.removeAt(index);
-
- if (mDisplayGroupIds.indexOfValue(displayGroupId) < 0) {
- // The DisplayGroup no longer exists; delete the DisplayPowerRequest.
- mDisplayPowerRequests.delete(displayGroupId);
- }
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- synchronized (mLock) {
- final int newDisplayGroupId = mDisplayManagerInternal.getDisplayGroupId(
- displayId);
- final int oldDisplayGroupId = mDisplayGroupIds.get(displayId);
-
- if (!mDisplayPowerRequests.contains(newDisplayGroupId)) {
- // A new DisplayGroup was created; create a new DisplayPowerRequest.
- mDisplayPowerRequests.append(newDisplayGroupId,
- new DisplayPowerRequest());
- }
- mDisplayGroupIds.put(displayId, newDisplayGroupId);
-
- if (mDisplayGroupIds.indexOfValue(oldDisplayGroupId) < 0) {
- // The DisplayGroup no longer exists; delete the DisplayPowerRequest.
- mDisplayPowerRequests.delete(oldDisplayGroupId);
- }
- }
- }
- };
-
- DisplayPowerRequestMapper(DisplayManager displayManager,
- DisplayManagerInternal displayManagerInternal, Handler handler) {
- mDisplayManagerInternal = displayManagerInternal;
- displayManager.registerDisplayListener(mDisplayListener, handler);
- mDisplayPowerRequests.append(Display.DEFAULT_DISPLAY_GROUP, new DisplayPowerRequest());
- mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY_GROUP);
- }
-
- DisplayPowerRequest get(int displayId) {
- synchronized (mLock) {
- return mDisplayPowerRequests.get(mDisplayGroupIds.get(displayId));
- }
- }
-}
diff --git a/services/core/java/com/android/server/power/FaceDownDetector.java b/services/core/java/com/android/server/power/FaceDownDetector.java
new file mode 100644
index 0000000..2442079
--- /dev/null
+++ b/services/core/java/com/android/server/power/FaceDownDetector.java
@@ -0,0 +1,456 @@
+/*
+ * 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.power;
+
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+
+import android.annotation.NonNull;
+import android.app.ActivityThread;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.io.PrintWriter;
+import java.time.Duration;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * Class used to detect when the phone is placed face down. This is used for Flip to Screen Off. A
+ * client can use this detector to trigger state changes like screen off when the phone is face
+ * down.
+ */
+public class FaceDownDetector implements SensorEventListener {
+
+ private static final String TAG = "FaceDownDetector";
+ private static final boolean DEBUG = false;
+
+ private static final int SCREEN_OFF_RESULT =
+ FrameworkStatsLog.FACE_DOWN_REPORTED__FACE_DOWN_RESPONSE__SCREEN_OFF;
+ private static final int USER_INTERACTION =
+ FrameworkStatsLog.FACE_DOWN_REPORTED__FACE_DOWN_RESPONSE__USER_INTERACTION;
+ private static final int UNFLIP =
+ FrameworkStatsLog.FACE_DOWN_REPORTED__FACE_DOWN_RESPONSE__UNFLIP;
+
+ /**
+ * Used by the ExponentialMovingAverage accelerations, this determines how quickly the
+ * average can change. A number closer to 1 will mean it will take longer to change.
+ */
+ private static final float MOVING_AVERAGE_WEIGHT = 0.5f;
+
+ /** DeviceConfig flag name, if {@code true}, enables Face Down features. */
+ private static final String KEY_FEATURE_ENABLED = "enable_flip_to_screen_off";
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ private static final boolean DEFAULT_FEATURE_ENABLED = true;
+
+ private boolean mIsEnabled;
+
+ /**
+ * DeviceConfig flag name, determines how long to disable sensor when user interacts while
+ * device is flipped.
+ */
+ private static final String KEY_INTERACTION_BACKOFF = "face_down_interaction_backoff_millis";
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ private static final long DEFAULT_INTERACTION_BACKOFF = 60_000;
+
+ private long mUserInteractionBackoffMillis;
+
+ /**
+ * DeviceConfig flag name, defines the max change in acceleration which will prevent face down
+ * due to movement.
+ */
+ static final String KEY_ACCELERATION_THRESHOLD = "acceleration_threshold";
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ static final float DEFAULT_ACCELERATION_THRESHOLD = 0.2f;
+
+ private float mAccelerationThreshold;
+
+ /**
+ * DeviceConfig flag name, defines the maximum z-axis acceleration that will indicate the phone
+ * is face down.
+ */
+ static final String KEY_Z_ACCELERATION_THRESHOLD = "z_acceleration_threshold";
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ static final float DEFAULT_Z_ACCELERATION_THRESHOLD = -9.5f;
+
+ private float mZAccelerationThreshold;
+
+ /**
+ * After going face down, we relax the threshold to make it more difficult to exit face down
+ * than to enter it.
+ */
+ private float mZAccelerationThresholdLenient;
+
+ /**
+ * DeviceConfig flag name, defines the minimum amount of time that has to pass while the phone
+ * is face down and not moving in order to trigger face down behavior, in milliseconds.
+ */
+ static final String KEY_TIME_THRESHOLD_MILLIS = "time_threshold_millis";
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ static final long DEFAULT_TIME_THRESHOLD_MILLIS = 1_000L;
+
+ private Duration mTimeThreshold;
+
+ private Sensor mAccelerometer;
+ private SensorManager mSensorManager;
+ private final Consumer<Boolean> mOnFlip;
+
+ /** Values we store for logging purposes. */
+ private long mLastFlipTime = 0L;
+ public int mPreviousResultType = 0;
+ public long mPreviousResultTime = 0L;
+ private long mMillisSaved = 0L;
+
+ private final ExponentialMovingAverage mCurrentXYAcceleration =
+ new ExponentialMovingAverage(MOVING_AVERAGE_WEIGHT);
+ private final ExponentialMovingAverage mCurrentZAcceleration =
+ new ExponentialMovingAverage(MOVING_AVERAGE_WEIGHT);
+
+ private boolean mFaceDown = false;
+ private boolean mActive = false;
+
+ private float mPrevAcceleration = 0;
+ private long mPrevAccelerationTime = 0;
+
+ private boolean mZAccelerationIsFaceDown = false;
+ private long mZAccelerationFaceDownTime = 0L;
+
+ private final Handler mHandler;
+ private final Runnable mUserActivityRunnable;
+
+ public FaceDownDetector(@NonNull Consumer<Boolean> onFlip) {
+ mOnFlip = Objects.requireNonNull(onFlip);
+ mHandler = new Handler(Looper.getMainLooper());
+ mUserActivityRunnable = () -> {
+ if (mFaceDown) {
+ exitFaceDown(USER_INTERACTION, SystemClock.uptimeMillis() - mLastFlipTime);
+ checkAndUpdateActiveState(false);
+ }
+ };
+ }
+
+ /** Initializes the FaceDownDetector and all necessary listeners. */
+ public void systemReady(Context context) {
+ mSensorManager = context.getSystemService(SensorManager.class);
+ mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ readValuesFromDeviceConfig();
+ checkAndUpdateActiveState(true);
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ ActivityThread.currentApplication().getMainExecutor(),
+ (properties) -> onDeviceConfigChange(properties.getKeyset()));
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ context.registerReceiver(new ScreenStateReceiver(), intentFilter);
+ }
+
+ /**
+ * Sets the active state of the detector. If false, we will not process accelerometer changes.
+ */
+ private void checkAndUpdateActiveState(boolean active) {
+ if (mIsEnabled && mActive != active) {
+ final long currentTime = SystemClock.uptimeMillis();
+ // Don't make active if there was recently a user interaction while face down.
+ if (active && mPreviousResultType == USER_INTERACTION
+ && currentTime - mPreviousResultTime < mUserInteractionBackoffMillis) {
+ return;
+ }
+ if (DEBUG) Slog.d(TAG, "Update active - " + active);
+ mActive = active;
+ if (!active) {
+ if (mFaceDown && mPreviousResultTime != USER_INTERACTION) {
+ mPreviousResultType = SCREEN_OFF_RESULT;
+ mPreviousResultTime = currentTime;
+ }
+ mSensorManager.unregisterListener(this);
+ mFaceDown = false;
+ mOnFlip.accept(false);
+ } else {
+ if (mPreviousResultType == SCREEN_OFF_RESULT) {
+ logScreenOff();
+ }
+ mSensorManager.registerListener(
+ this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ }
+ }
+
+ /** Prints state information about FaceDownDetector */
+ public void dump(PrintWriter pw) {
+ pw.println("FaceDownDetector:");
+ pw.println(" mFaceDown=" + mFaceDown);
+ pw.println(" mActive=" + mActive);
+ pw.println(" mLastFlipTime=" + mLastFlipTime);
+ pw.println(" mUserInteractionBackoffMillis=" + mUserInteractionBackoffMillis);
+ pw.println(" mPreviousResultTime=" + mPreviousResultTime);
+ pw.println(" mPreviousResultType=" + mPreviousResultType);
+ pw.println(" mMillisSaved=" + mMillisSaved);
+ pw.println(" mZAccelerationThreshold=" + mZAccelerationThreshold);
+ pw.println(" mAccelerationThreshold=" + mAccelerationThreshold);
+ pw.println(" mTimeThreshold=" + mTimeThreshold);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) return;
+ if (!mActive || !mIsEnabled) return;
+
+ final float x = event.values[0];
+ final float y = event.values[1];
+ mCurrentXYAcceleration.updateMovingAverage(x * x + y * y);
+ mCurrentZAcceleration.updateMovingAverage(event.values[2]);
+
+ // Detect movement
+ // If the x, y acceleration is within the acc threshold for at least a length of time longer
+ // than the time threshold, we set moving to true.
+ final long curTime = event.timestamp;
+ if (Math.abs(mCurrentXYAcceleration.mMovingAverage - mPrevAcceleration)
+ > mAccelerationThreshold) {
+ mPrevAcceleration = mCurrentXYAcceleration.mMovingAverage;
+ mPrevAccelerationTime = curTime;
+ }
+ final boolean moving = curTime - mPrevAccelerationTime <= mTimeThreshold.toNanos();
+
+ // If the z acceleration is beyond the gravity/z-acceleration threshold for at least a
+ // length of time longer than the time threshold, we set isFaceDownForPeriod to true.
+ final float zAccelerationThreshold =
+ mFaceDown ? mZAccelerationThresholdLenient : mZAccelerationThreshold;
+ final boolean isCurrentlyFaceDown =
+ mCurrentZAcceleration.mMovingAverage < zAccelerationThreshold;
+ final boolean isFaceDownForPeriod = isCurrentlyFaceDown
+ && mZAccelerationIsFaceDown
+ && curTime - mZAccelerationFaceDownTime > mTimeThreshold.toNanos();
+ if (isCurrentlyFaceDown && !mZAccelerationIsFaceDown) {
+ mZAccelerationFaceDownTime = curTime;
+ mZAccelerationIsFaceDown = true;
+ } else if (!isCurrentlyFaceDown) {
+ mZAccelerationIsFaceDown = false;
+ }
+
+
+ if (!moving && isFaceDownForPeriod && !mFaceDown) {
+ faceDownDetected();
+ } else if (!isFaceDownForPeriod && mFaceDown) {
+ unFlipDetected();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ private void faceDownDetected() {
+ if (DEBUG) Slog.d(TAG, "Triggered faceDownDetected.");
+ mLastFlipTime = SystemClock.uptimeMillis();
+ mFaceDown = true;
+ mOnFlip.accept(true);
+ }
+
+ private void unFlipDetected() {
+ if (DEBUG) Slog.d(TAG, "Triggered exitFaceDown");
+ exitFaceDown(UNFLIP, SystemClock.uptimeMillis() - mLastFlipTime);
+ }
+
+ /**
+ * The user interacted with the screen while face down, indicated the phone is in use.
+ * We log this event and temporarily make this detector inactive.
+ */
+ public void userActivity() {
+ mHandler.post(mUserActivityRunnable);
+ }
+
+ private void exitFaceDown(int resultType, long millisSinceFlip) {
+ FrameworkStatsLog.write(FrameworkStatsLog.FACE_DOWN_REPORTED,
+ resultType,
+ millisSinceFlip,
+ /* millis_until_normal_timeout= */ 0L,
+ /* millis_until_next_screen_on= */ 0L);
+ mFaceDown = false;
+ mLastFlipTime = 0L;
+ mPreviousResultType = resultType;
+ mPreviousResultTime = SystemClock.uptimeMillis();
+ mOnFlip.accept(false);
+ }
+
+ private void logScreenOff() {
+ if (mPreviousResultType == SCREEN_OFF_RESULT) {
+ final long currentTime = SystemClock.uptimeMillis();
+ FrameworkStatsLog.write(FrameworkStatsLog.FACE_DOWN_REPORTED,
+ mPreviousResultType,
+ /* millis_since_flip= */ mPreviousResultTime - mLastFlipTime,
+ mMillisSaved,
+ /* millis_until_next_screen_on= */ currentTime - mPreviousResultTime);
+ mPreviousResultType = -1;
+ }
+ }
+
+ private boolean isEnabled() {
+ return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_FEATURE_ENABLED,
+ DEFAULT_FEATURE_ENABLED);
+ }
+
+ private float getAccelerationThreshold() {
+ return getFloatFlagValue(KEY_ACCELERATION_THRESHOLD,
+ DEFAULT_ACCELERATION_THRESHOLD,
+ -2.0f,
+ 2.0f);
+ }
+
+ private float getZAccelerationThreshold() {
+ return getFloatFlagValue(KEY_Z_ACCELERATION_THRESHOLD,
+ DEFAULT_Z_ACCELERATION_THRESHOLD,
+ -15.0f,
+ 0.0f);
+ }
+
+ private long getUserInteractionBackoffMillis() {
+ return getLongFlagValue(KEY_INTERACTION_BACKOFF,
+ DEFAULT_INTERACTION_BACKOFF,
+ 0,
+ 3600_000);
+ }
+
+ private float getFloatFlagValue(String key, float defaultValue, float min, float max) {
+ final float value = DeviceConfig.getFloat(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ key,
+ defaultValue);
+
+ if (value < min || value > max) {
+ Slog.w(TAG, "Bad flag value supplied for: " + key);
+ return defaultValue;
+ }
+
+ return value;
+ }
+
+ private long getLongFlagValue(String key, long defaultValue, long min, long max) {
+ final long value = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ key,
+ defaultValue);
+
+ if (value < min || value > max) {
+ Slog.w(TAG, "Bad flag value supplied for: " + key);
+ return defaultValue;
+ }
+
+ return value;
+ }
+
+ private Duration getTimeThreshold() {
+ final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_TIME_THRESHOLD_MILLIS,
+ DEFAULT_TIME_THRESHOLD_MILLIS);
+
+ if (millis < 0 || millis > 15_000) {
+ Slog.w(TAG, "Bad flag value supplied for: " + KEY_TIME_THRESHOLD_MILLIS);
+ return Duration.ofMillis(DEFAULT_TIME_THRESHOLD_MILLIS);
+ }
+
+ return Duration.ofMillis(millis);
+ }
+
+ private void onDeviceConfigChange(@NonNull Set<String> keys) {
+ for (String key : keys) {
+ switch (key) {
+ case KEY_ACCELERATION_THRESHOLD:
+ case KEY_Z_ACCELERATION_THRESHOLD:
+ case KEY_TIME_THRESHOLD_MILLIS:
+ case KEY_FEATURE_ENABLED:
+ readValuesFromDeviceConfig();
+ return;
+ default:
+ Slog.i(TAG, "Ignoring change on " + key);
+ }
+ }
+ }
+
+ private void readValuesFromDeviceConfig() {
+ mAccelerationThreshold = getAccelerationThreshold();
+ mZAccelerationThreshold = getZAccelerationThreshold();
+ mZAccelerationThresholdLenient = mZAccelerationThreshold + 1.0f;
+ mTimeThreshold = getTimeThreshold();
+ mIsEnabled = isEnabled();
+ mUserInteractionBackoffMillis = getUserInteractionBackoffMillis();
+
+ Slog.i(TAG, "readValuesFromDeviceConfig():"
+ + "\nmAccelerationThreshold=" + mAccelerationThreshold
+ + "\nmZAccelerationThreshold=" + mZAccelerationThreshold
+ + "\nmTimeThreshold=" + mTimeThreshold
+ + "\nmIsEnabled=" + mIsEnabled);
+ }
+
+ /**
+ * Sets how much screen on time might be saved as a result of this detector. Currently used for
+ * logging purposes.
+ */
+ public void setMillisSaved(long millisSaved) {
+ mMillisSaved = millisSaved;
+ }
+
+ private final class ScreenStateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ checkAndUpdateActiveState(false);
+ } else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+ checkAndUpdateActiveState(true);
+ }
+ }
+ }
+
+ private final class ExponentialMovingAverage {
+ private final float mAlpha;
+ private final float mInitialAverage;
+ private float mMovingAverage;
+
+ ExponentialMovingAverage(float alpha) {
+ this(alpha, 0.0f);
+ }
+
+ ExponentialMovingAverage(float alpha, float initialAverage) {
+ this.mAlpha = alpha;
+ this.mInitialAverage = initialAverage;
+ this.mMovingAverage = initialAverage;
+ }
+
+ void updateMovingAverage(float newValue) {
+ mMovingAverage = newValue + mAlpha * (mMovingAverage - newValue);
+ }
+
+ void reset() {
+ mMovingAverage = this.mInitialAverage;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index b8e0156..f49e2f1 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -119,6 +119,7 @@
private final AppOpsManager mAppOps;
private final SuspendBlocker mSuspendBlocker;
private final WindowManagerPolicy mPolicy;
+ private final FaceDownDetector mFaceDownDetector;
private final ActivityManagerInternal mActivityManagerInternal;
private final InputManagerInternal mInputManagerInternal;
private final InputMethodManagerInternal mInputMethodManagerInternal;
@@ -165,12 +166,14 @@
private boolean mUserActivityPending;
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
- SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
+ FaceDownDetector faceDownDetector) {
mContext = context;
mBatteryStats = batteryStats;
mAppOps = mContext.getSystemService(AppOpsManager.class);
mSuspendBlocker = suspendBlocker;
mPolicy = policy;
+ mFaceDownDetector = faceDownDetector;
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
@@ -654,6 +657,7 @@
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
tm.notifyUserActivity();
mPolicy.userActivity();
+ mFaceDownDetector.userActivity();
}
void postEnhancedDischargePredictionBroadcast(long delayMs) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index c0b8202..8c46445 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -16,8 +16,13 @@
package com.android.server.power;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.policyToString;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
+import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF;
+import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED;
+import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_ADDED;
+import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_TURNED_ON;
import static android.os.PowerManagerInternal.MODE_DEVICE_IDLE;
import static android.os.PowerManagerInternal.MODE_DISPLAY_INACTIVE;
import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
@@ -42,7 +47,6 @@
import android.hardware.SensorManager;
import android.hardware.SystemSensorManager;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.hardware.power.Boost;
@@ -176,6 +180,10 @@
private static final int DIRTY_VR_MODE_CHANGED = 1 << 13;
// Dirty bit: attentive timer may have timed out
private static final int DIRTY_ATTENTIVE = 1 << 14;
+ // Dirty bit: phone flipped to face down
+ private static final int DIRTY_FACE_DOWN = 1 << 15;
+ // Dirty bit: display group power state has changed
+ private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 16;
// Summarizes the state of all active wakelocks.
private static final int WAKE_LOCK_CPU = 1 << 0;
@@ -263,6 +271,7 @@
private final BatterySaverStateMachine mBatterySaverStateMachine;
private final BatterySavingStats mBatterySavingStats;
private final AttentionDetector mAttentionDetector;
+ private final FaceDownDetector mFaceDownDetector;
private final BinderService mBinderService;
private final LocalService mLocalService;
private final NativeWrapper mNativeWrapper;
@@ -297,10 +306,6 @@
private int mWakefulnessRaw;
private boolean mWakefulnessChanging;
- // True if the sandman has just been summoned for the first time since entering the
- // dreaming or dozing state. Indicates whether a new dream should begin.
- private boolean mSandmanSummoned;
-
// True if MSG_SANDMAN has been scheduled.
private boolean mSandmanScheduled;
@@ -351,11 +356,7 @@
// Manages the desired power state of displays. The actual state may lag behind the
// requested because it is updated asynchronously by the display power controller.
- private DisplayPowerRequestMapper mDisplayPowerRequestMapper;
-
- // True if the display power state has been fully applied, which means the display
- // is actually on or actually off or whatever was requested.
- private boolean mDisplayReady;
+ private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper;
// The suspend blocker used to keep the CPU alive when an application has acquired
// a wake lock.
@@ -547,6 +548,10 @@
public final float mScreenBrightnessMaximumVr;
public final float mScreenBrightnessDefaultVr;
+ // Value we store for tracking face down behavior.
+ private boolean mIsFaceDown = false;
+ private long mLastFlipTime = 0L;
+
// The screen brightness mode.
// One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants.
private int mScreenBrightnessModeSetting;
@@ -625,6 +630,39 @@
// but the DreamService has not yet been told to start (it's an async process).
private boolean mDozeStartInProgress;
+ private final class DisplayGroupPowerChangeListener implements
+ DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener {
+ @Override
+ public void onDisplayGroupEventLocked(int event, int groupId) {
+ final int oldWakefulness = getWakefulnessLocked();
+ final int newWakefulness = mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked();
+ if (oldWakefulness != newWakefulness) {
+ final int reason;
+ switch (newWakefulness) {
+ case WAKEFULNESS_AWAKE:
+ reason = event == DISPLAY_GROUP_ADDED ? WAKE_REASON_DISPLAY_GROUP_ADDED
+ : WAKE_REASON_DISPLAY_GROUP_TURNED_ON;
+ break;
+ case WAKEFULNESS_DOZING:
+ reason = event == DISPLAY_GROUP_REMOVED
+ ? GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED
+ : GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF;
+ break;
+ default:
+ reason = 0;
+ }
+
+ setGlobalWakefulnessLocked(
+ mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(),
+ mClock.uptimeMillis(), reason, Process.SYSTEM_UID, Process.SYSTEM_UID,
+ mContext.getOpPackageName(), "groupId: " + groupId);
+ }
+
+ mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
+ updatePowerStateLocked();
+ }
+ }
+
private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver {
@Override
public void onUserSwitching(@UserIdInt int newUserId) throws RemoteException {
@@ -791,8 +829,10 @@
@VisibleForTesting
static class Injector {
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
- SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
- return new Notifier(looper, context, batteryStats, suspendBlocker, policy);
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
+ FaceDownDetector faceDownDetector) {
+ return new Notifier(
+ looper, context, batteryStats, suspendBlocker, policy, faceDownDetector);
}
SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
@@ -868,6 +908,12 @@
void invalidateIsInteractiveCaches() {
PowerManager.invalidateIsInteractiveCaches();
}
+
+ DisplayGroupPowerStateMapper createDisplayPowerRequestMapper(Object lock,
+ DisplayManagerInternal displayManagerInternal,
+ DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener listener) {
+ return new DisplayGroupPowerStateMapper(lock, displayManagerInternal, listener);
+ }
}
final Constants mConstants;
@@ -906,6 +952,7 @@
mAmbientDisplaySuppressionController =
mInjector.createAmbientDisplaySuppressionController(context);
mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock);
+ mFaceDownDetector = new FaceDownDetector(this::onFlip);
mBatterySavingStats = new BatterySavingStats(mLock);
mBatterySaverPolicy =
@@ -1011,6 +1058,26 @@
}
}
+ private void onFlip(boolean isFaceDown) {
+ long millisUntilNormalTimeout = 0;
+ synchronized (mLock) {
+ mIsFaceDown = isFaceDown;
+ if (isFaceDown) {
+ final long currentTime = mClock.uptimeMillis();
+ mLastFlipTime = currentTime;
+ final long sleepTimeout = getSleepTimeoutLocked(-1L);
+ final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, -1L);
+ millisUntilNormalTimeout =
+ mLastUserActivityTime + screenOffTimeout - mClock.uptimeMillis();
+ mDirty |= DIRTY_FACE_DOWN;
+ updatePowerStateLocked();
+ }
+ }
+ if (isFaceDown) {
+ mFaceDownDetector.setMillisSaved(millisUntilNormalTimeout);
+ }
+ }
+
@Override
public void onStart() {
publishBinderService(Context.POWER_SERVICE, mBinderService, /* allowIsolated= */ false,
@@ -1038,7 +1105,8 @@
updatePowerStateLocked();
if (sQuiescent) {
- goToSleepNoUpdateLocked(mClock.uptimeMillis(),
+ sleepDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP,
+ mClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
}
@@ -1055,8 +1123,8 @@
mPolicy = getLocalService(WindowManagerPolicy.class);
mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
mAttentionDetector.systemReady(mContext);
- mDisplayPowerRequestMapper = new DisplayPowerRequestMapper(mContext.getSystemService(
- DisplayManager.class), mDisplayManagerInternal, mHandler);
+ mDisplayGroupPowerStateMapper = mInjector.createDisplayPowerRequestMapper(mLock,
+ mDisplayManagerInternal, new DisplayGroupPowerChangeListener());
SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
@@ -1065,7 +1133,7 @@
mBatteryStats = BatteryStatsService.getService();
mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
- mPolicy);
+ mPolicy, mFaceDownDetector);
mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager,
mInjector.createSuspendBlocker(
@@ -1099,6 +1167,7 @@
mBatterySaverController.systemReady();
mBatterySaverPolicy.systemReady();
+ mFaceDownDetector.systemReady(mContext);
// Register for settings changes.
resolver.registerContentObserver(Settings.Secure.getUriFor(
@@ -1373,9 +1442,11 @@
opPackageName = wakeLock.mPackageName;
opUid = wakeLock.mOwnerUid;
}
- wakeUpNoUpdateLocked(mClock.uptimeMillis(),
- PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
- opUid, opPackageName, opUid);
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ wakeDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
+ opUid, opPackageName, opUid);
+ }
}
}
@@ -1664,26 +1735,29 @@
}
}
- private void wakeUpInternal(long eventTime, @WakeReason int reason, String details, int uid,
- String opPackageName, int opUid) {
+ private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason,
+ String details, int uid, String opPackageName, int opUid) {
synchronized (mLock) {
- if (wakeUpNoUpdateLocked(eventTime, reason, details, uid, opPackageName, opUid)) {
+ if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid,
+ opPackageName, opUid)) {
updatePowerStateLocked();
}
}
}
- private boolean wakeUpNoUpdateLocked(long eventTime, @WakeReason int reason, String details,
- int reasonUid, String opPackageName, int opUid) {
+ private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime,
+ @WakeReason int reason, String details, int uid, String opPackageName, int opUid) {
if (DEBUG_SPEW) {
- Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid);
+ Slog.d(TAG, "wakeDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+ + ", groupId=" + groupId + ", uid=" + uid);
}
if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) {
return false;
}
- if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
+ final int currentState = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+ if (currentState == WAKEFULNESS_AWAKE) {
if (!mBootCompleted && sQuiescent) {
mDirty |= DIRTY_QUIESCENT;
return true;
@@ -1691,113 +1765,90 @@
return false;
}
- Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
-
- Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOnDisplay");
try {
- Slog.i(TAG, "Waking up from "
- + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked())
- + " (uid=" + reasonUid
+ Slog.i(TAG, "Powering on display group from"
+ + PowerManagerInternal.wakefulnessToString(currentState)
+ + " (groupId=" + groupId
+ + ", uid=" + uid
+ ", reason=" + PowerManager.wakeReasonToString(reason)
+ ", details=" + details
+ ")...");
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
- mLastWakeTime = eventTime;
- mLastWakeReason = reason;
- setWakefulnessLocked(WAKEFULNESS_AWAKE, reason, eventTime);
-
- mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid);
- userActivityNoUpdateLocked(
- eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
-
- if (sQuiescent) {
- mDirty |= DIRTY_QUIESCENT;
- }
+ setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
+ opPackageName, details);
+ mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime);
+ mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
+
return true;
}
- private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
+ private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags,
+ int uid) {
synchronized (mLock) {
- if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
+ if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) {
updatePowerStateLocked();
}
}
}
- /**
- * Puts the system in doze.
- *
- * This method is called goToSleep for historical reasons but actually attempts to DOZE,
- * and only tucks itself in to SLEEP if requested with the flag
- * {@link PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE}.
- */
- @SuppressWarnings("deprecation")
- private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
+ private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason,
+ int flags, int uid) {
if (DEBUG_SPEW) {
- Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime
- + ", reason=" + reason + ", flags=" + flags + ", uid=" + uid);
+ Slog.d(TAG, "sleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+ + ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags
+ + ", uid=" + uid);
}
if (eventTime < mLastWakeTime
- || getWakefulnessLocked() == WAKEFULNESS_ASLEEP
- || getWakefulnessLocked() == WAKEFULNESS_DOZING
+ || !PowerManagerInternal.isInteractive(getWakefulnessLocked())
|| !mSystemReady
|| !mBootCompleted) {
return false;
}
- Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");
+ final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+ if (!PowerManagerInternal.isInteractive(wakefulness)) {
+ return false;
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOffDisplay");
try {
reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,
Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));
- Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)
- + " (uid " + uid + ")...");
+ Slog.i(TAG, "Powering off display group due to "
+ + PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId
+ + ", uid= " + uid + ")...");
- mLastSleepTime = eventTime;
- mLastSleepReason = reason;
- mSandmanSummoned = true;
- mDozeStartInProgress = true;
- setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime);
-
- // Report the number of wake locks that will be cleared by going to sleep.
- int numWakeLocksCleared = 0;
- final int numWakeLocks = mWakeLocks.size();
- for (int i = 0; i < numWakeLocks; i++) {
- final WakeLock wakeLock = mWakeLocks.get(i);
- switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
- case PowerManager.FULL_WAKE_LOCK:
- case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
- case PowerManager.SCREEN_DIM_WAKE_LOCK:
- numWakeLocksCleared += 1;
- break;
- }
- }
- EventLogTags.writePowerSleepRequested(numWakeLocksCleared);
-
- // Skip dozing if requested.
+ mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true);
+ setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason,
+ /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
- reallyGoToSleepNoUpdateLocked(eventTime, uid);
+ reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid);
}
+ mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;
}
- private void napInternal(long eventTime, int uid) {
+ private void dreamDisplayGroup(int groupId, long eventTime, int uid) {
synchronized (mLock) {
- if (napNoUpdateLocked(eventTime, uid)) {
+ if (dreamDisplayGroupNoUpdateLocked(groupId, eventTime, uid)) {
updatePowerStateLocked();
}
}
}
- private boolean napNoUpdateLocked(long eventTime, int uid) {
+ private boolean dreamDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) {
if (DEBUG_SPEW) {
- Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid);
+ Slog.d(TAG, "dreamDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+ + ", uid=" + uid);
}
if (eventTime < mLastWakeTime || getWakefulnessLocked() != WAKEFULNESS_AWAKE
@@ -1805,36 +1856,42 @@
return false;
}
- Trace.traceBegin(Trace.TRACE_TAG_POWER, "nap");
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "napDisplayGroup");
try {
- Slog.i(TAG, "Nap time (uid " + uid +")...");
+ Slog.i(TAG, "Napping display group (groupId=" + groupId + ", uid=" + uid + ")...");
- mSandmanSummoned = true;
- setWakefulnessLocked(WAKEFULNESS_DREAMING, 0, eventTime);
+ mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true);
+ setWakefulnessLocked(groupId, WAKEFULNESS_DREAMING, eventTime, uid, /* reason= */
+ 0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
+ mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
+
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;
}
- // Done dozing, drop everything and go to sleep.
- private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) {
+ private boolean reallySleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) {
if (DEBUG_SPEW) {
- Slog.d(TAG, "reallyGoToSleepNoUpdateLocked: eventTime=" + eventTime
+ Slog.d(TAG, "reallySleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+ ", uid=" + uid);
}
if (eventTime < mLastWakeTime || getWakefulnessLocked() == WAKEFULNESS_ASLEEP
- || !mBootCompleted || !mSystemReady) {
+ || !mBootCompleted || !mSystemReady
+ || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId)
+ == WAKEFULNESS_ASLEEP) {
return false;
}
- Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallyGoToSleep");
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallySleepDisplayGroup");
try {
- Slog.i(TAG, "Sleeping (uid " + uid +")...");
+ Slog.i(TAG, "Sleeping display group (groupId=" + groupId + ", uid=" + uid + ")...");
- setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
- eventTime);
+ setWakefulnessLocked(groupId, WAKEFULNESS_ASLEEP, eventTime, uid,
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, /* opUid= */ 0,
+ /* opPackageName= */ null, /* details= */ null);
+ mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -1842,8 +1899,62 @@
}
@VisibleForTesting
- void setWakefulnessLocked(int wakefulness, int reason, long eventTime) {
- if (getWakefulnessLocked() != wakefulness) {
+ void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason,
+ int opUid, String opPackageName, String details) {
+ if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) {
+ setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(),
+ eventTime, reason, uid, opUid, opPackageName, details);
+ }
+ }
+
+ private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid,
+ int opUid, String opPackageName, String details) {
+ if (getWakefulnessLocked() == wakefulness) {
+ return;
+ }
+
+ // Phase 1: Handle pre-wakefulness change bookkeeping.
+ final String traceMethodName;
+ switch (wakefulness) {
+ case WAKEFULNESS_ASLEEP:
+ traceMethodName = "reallyGoToSleep";
+ Slog.i(TAG, "Sleeping (uid " + uid + ")...");
+ break;
+
+ case WAKEFULNESS_AWAKE:
+ traceMethodName = "wakeUp";
+ Slog.i(TAG, "Waking up from "
+ + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked())
+ + " (uid=" + uid
+ + ", reason=" + PowerManager.wakeReasonToString(reason)
+ + ", details=" + details
+ + ")...");
+ mLastWakeTime = eventTime;
+ mLastWakeReason = reason;
+ break;
+
+ case WAKEFULNESS_DREAMING:
+ traceMethodName = "nap";
+ Slog.i(TAG, "Nap time (uid " + uid + ")...");
+ break;
+
+ case WAKEFULNESS_DOZING:
+ traceMethodName = "goToSleep";
+ Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)
+ + " (uid " + uid + ")...");
+
+ mLastSleepTime = eventTime;
+ mLastSleepReason = reason;
+ mDozeStartInProgress = true;
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unexpected wakefulness: " + wakefulness);
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, traceMethodName);
+ try {
+ // Phase 2: Handle wakefulness change and bookkeeping.
// Under lock, invalidate before set ensures caches won't return stale values.
mInjector.invalidateIsInteractiveCaches();
mWakefulnessRaw = wakefulness;
@@ -1857,6 +1968,37 @@
mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime);
}
mAttentionDetector.onWakefulnessChangeStarted(wakefulness);
+
+ // Phase 3: Handle post-wakefulness change bookkeeping.
+ switch (wakefulness) {
+ case WAKEFULNESS_AWAKE:
+ mNotifier.onWakeUp(reason, details, uid, opPackageName, opUid);
+ userActivityNoUpdateLocked(
+ eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+ if (sQuiescent) {
+ mDirty |= DIRTY_QUIESCENT;
+ }
+ break;
+
+ case WAKEFULNESS_DOZING:
+ // Report the number of wake locks that will be cleared by going to sleep.
+ int numWakeLocksCleared = 0;
+ final int numWakeLocks = mWakeLocks.size();
+ for (int i = 0; i < numWakeLocks; i++) {
+ final WakeLock wakeLock = mWakeLocks.get(i);
+ switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ case PowerManager.FULL_WAKE_LOCK:
+ case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ case PowerManager.SCREEN_DIM_WAKE_LOCK:
+ numWakeLocksCleared += 1;
+ break;
+ }
+ }
+ EventLogTags.writePowerSleepRequested(numWakeLocksCleared);
+ break;
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
@@ -1879,7 +2021,7 @@
}
private void finishWakefulnessChangeIfNeededLocked() {
- if (mWakefulnessChanging && mDisplayReady) {
+ if (mWakefulnessChanging && mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
if (getWakefulnessLocked() == WAKEFULNESS_DOZING
&& (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
return; // wait until dream has enabled dozing
@@ -1891,13 +2033,6 @@
|| getWakefulnessLocked() == WAKEFULNESS_ASLEEP) {
logSleepTimeoutRecapturedLocked();
}
- if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
- final int latencyMs = (int) (mClock.uptimeMillis() - mLastWakeTime);
- if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
- Slog.w(TAG, "Screen on took " + latencyMs + " ms");
- }
- }
mWakefulnessChanging = false;
mNotifier.onWakefulnessChangeFinished();
}
@@ -1996,7 +2131,6 @@
if ((dirty & DIRTY_BATTERY_STATE) != 0) {
final boolean wasPowered = mIsPowered;
final int oldPlugType = mPlugType;
- final boolean oldLevelLow = mBatteryLevelLow;
mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
mPlugType = mBatteryManagerInternal.getPlugType();
mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
@@ -2025,7 +2159,8 @@
final long now = mClock.uptimeMillis();
if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
dockedOnWirelessCharger)) {
- wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN,
+ wakeDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, now,
+ PowerManager.WAKE_REASON_PLUGGED_IN,
"android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
}
@@ -2283,9 +2418,11 @@
|| getWakefulnessLocked() == WAKEFULNESS_DOZING) {
final long attentiveTimeout = getAttentiveTimeoutLocked();
final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
- final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout,
+ long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout,
attentiveTimeout);
final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
+ screenOffTimeout =
+ getScreenOffTimeoutWithFaceDownLocked(screenOffTimeout, screenDimDuration);
final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
final long nextProfileTimeout = getNextProfileTimeoutLocked(now);
@@ -2307,7 +2444,8 @@
nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
if (now < nextTimeout) {
final DisplayPowerRequest displayPowerRequest =
- mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY);
+ mDisplayGroupPowerStateMapper.getPowerRequestLocked(
+ Display.DEFAULT_DISPLAY_GROUP);
if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
|| displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
@@ -2541,6 +2679,16 @@
(long)(screenOffTimeout * mMaximumScreenDimRatioConfig));
}
+ private long getScreenOffTimeoutWithFaceDownLocked(
+ long screenOffTimeout, long screenDimDuration) {
+ // If face down, we decrease the timeout to equal the dim duration so that the
+ // device will go into a dim state.
+ if (mIsFaceDown) {
+ return Math.min(screenDimDuration, screenOffTimeout);
+ }
+ return screenOffTimeout;
+ }
+
/**
* Updates the wakefulness of the device.
*
@@ -2562,13 +2710,23 @@
}
final long time = mClock.uptimeMillis();
if (isAttentiveTimeoutExpired(time)) {
- changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
- PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
+ // TODO (b/175764389): Support per-display timeouts.
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ changed = sleepDisplayGroupNoUpdateLocked(id, time,
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
+ }
} else if (shouldNapAtBedTimeLocked()) {
- changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
+ // TODO (b/175764389): Support per-display timeouts.
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID);
+ }
} else {
- changed = goToSleepNoUpdateLocked(time,
- PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
+ // TODO (b/175764389): Support per-display timeouts.
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ changed = sleepDisplayGroupNoUpdateLocked(id, time,
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
+ }
}
}
}
@@ -2643,6 +2801,7 @@
private void updateDreamLocked(int dirty, boolean displayBecameReady) {
if ((dirty & (DIRTY_WAKEFULNESS
| DIRTY_USER_ACTIVITY
+ | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED
| DIRTY_ATTENTIVE
| DIRTY_WAKE_LOCKS
| DIRTY_BOOT_COMPLETED
@@ -2651,7 +2810,7 @@
| DIRTY_STAY_ON
| DIRTY_PROXIMITY_POSITIVE
| DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) {
- if (mDisplayReady) {
+ if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
scheduleSandmanLocked();
}
}
@@ -2666,6 +2825,14 @@
}
}
+ private void handleSandman() {
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ if (mDisplayGroupPowerStateMapper.isSandmanSupported(id)) {
+ handleSandman(id);
+ }
+ }
+ }
+
/**
* Called when the device enters or exits a dreaming or dozing state.
*
@@ -2673,16 +2840,19 @@
* the dream and we don't want to hold our lock while doing so. There is a risk that
* the device will wake or go to sleep in the meantime so we have to handle that case.
*/
- private void handleSandman() { // runs on handler thread
+ private void handleSandman(int groupId) { // runs on handler thread
// Handle preconditions.
final boolean startDreaming;
final int wakefulness;
synchronized (mLock) {
mSandmanScheduled = false;
+ // TODO (b/175764708): Support per-display doze.
wakefulness = getWakefulnessLocked();
- if (mSandmanSummoned && mDisplayReady) {
- startDreaming = canDreamLocked() || canDozeLocked();
- mSandmanSummoned = false;
+ if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) &&
+ mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId)
+ && mDisplayGroupPowerStateMapper.isReady(groupId)) {
+ startDreaming = canDreamLocked(groupId) || canDozeLocked();
+ mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, false);
} else {
startDreaming = false;
}
@@ -2721,14 +2891,15 @@
// If preconditions changed, wait for the next iteration to determine
// whether the dream should continue (or be restarted).
- if (mSandmanSummoned || getWakefulnessLocked() != wakefulness) {
+ if (mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId)
+ || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != wakefulness) {
return; // wait for next cycle
}
// Determine whether the dream should continue.
long now = mClock.uptimeMillis();
if (wakefulness == WAKEFULNESS_DREAMING) {
- if (isDreaming && canDreamLocked()) {
+ if (isDreaming && canDreamLocked(groupId)) {
if (mDreamsBatteryLevelDrainCutoffConfig >= 0
&& mBatteryLevel < mBatteryLevelWhenDreamStarted
- mDreamsBatteryLevelDrainCutoffConfig
@@ -2748,16 +2919,13 @@
// Dream has ended or will be stopped. Update the power state.
if (isItBedTimeYetLocked()) {
- int flags = 0;
- if (isAttentiveTimeoutExpired(now)) {
- flags |= PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE;
- }
- goToSleepNoUpdateLocked(now, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags,
- Process.SYSTEM_UID);
+ final int flags = isAttentiveTimeoutExpired(now)
+ ? PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE : 0;
+ sleepDisplayGroupNoUpdateLocked(groupId, now,
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, Process.SYSTEM_UID);
updatePowerStateLocked();
} else {
- wakeUpNoUpdateLocked(now,
- PowerManager.WAKE_REASON_UNKNOWN,
+ wakeDisplayGroupNoUpdateLocked(groupId, now, PowerManager.WAKE_REASON_UNKNOWN,
"android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
updatePowerStateLocked();
@@ -2768,7 +2936,7 @@
}
// Doze has ended or will be stopped. Update the power state.
- reallyGoToSleepNoUpdateLocked(now, Process.SYSTEM_UID);
+ reallySleepDisplayGroupNoUpdateLocked(groupId, now, Process.SYSTEM_UID);
updatePowerStateLocked();
}
}
@@ -2780,11 +2948,11 @@
}
/**
- * Returns true if the device is allowed to dream in its current state.
+ * Returns true if the {@code groupId} is allowed to dream in its current state.
*/
- private boolean canDreamLocked() {
+ private boolean canDreamLocked(int groupId) {
final DisplayPowerRequest displayPowerRequest =
- mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY);
+ mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId);
if (getWakefulnessLocked() != WAKEFULNESS_DREAMING
|| !mDreamsSupportedConfig
|| !mDreamsEnabledSetting
@@ -2822,95 +2990,117 @@
/**
* Updates the display power state asynchronously.
- * When the update is finished, mDisplayReady will be set to true. The display
- * controller posts a message to tell us when the actual display power state
+ * When the update is finished, the ready state of the displays will be updated. The display
+ * controllers post a message to tell us when the actual display power state
* has been updated so we come back here to double-check and finish up.
*
* This function recalculates the display power state each time.
*
- * @return true if the display became ready.
+ * @return {@code true} if all displays became ready; {@code false} otherwise
*/
private boolean updateDisplayPowerStateLocked(int dirty) {
- final boolean oldDisplayReady = mDisplayReady;
+ final boolean oldDisplayReady = mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked();
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
- DIRTY_QUIESCENT)) != 0) {
+ DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_POWER_UPDATED)) != 0) {
if ((dirty & DIRTY_QUIESCENT) != 0) {
- if (mDisplayReady) {
+ if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
sQuiescent = false;
} else {
mDirty |= DIRTY_QUIESCENT;
}
}
- final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
- Display.DEFAULT_DISPLAY);
- displayPowerRequest.policy = getDesiredScreenPolicyLocked();
+ for (final int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ final DisplayPowerRequest displayPowerRequest =
+ mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId);
+ displayPowerRequest.policy = getDesiredScreenPolicyLocked(groupId);
- // Determine appropriate screen brightness and auto-brightness adjustments.
- final boolean autoBrightness;
- final float screenBrightnessOverride;
- if (!mBootCompleted) {
- // Keep the brightness steady during boot. This requires the
- // bootloader brightness and the default brightness to be identical.
- autoBrightness = false;
- screenBrightnessOverride = mScreenBrightnessDefault;
- } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
- autoBrightness = false;
- screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager;
- } else {
- autoBrightness = (mScreenBrightnessModeSetting ==
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
- screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- }
+ // Determine appropriate screen brightness and auto-brightness adjustments.
+ final boolean autoBrightness;
+ final float screenBrightnessOverride;
+ if (!mBootCompleted) {
+ // Keep the brightness steady during boot. This requires the
+ // bootloader brightness and the default brightness to be identical.
+ autoBrightness = false;
+ screenBrightnessOverride = mScreenBrightnessDefault;
+ } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
+ autoBrightness = false;
+ screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager;
+ } else {
+ autoBrightness = (mScreenBrightnessModeSetting
+ == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
- // Update display power request.
- displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
- displayPowerRequest.useAutoBrightness = autoBrightness;
- displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
- displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
+ // Update display power request.
+ displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
+ displayPowerRequest.useAutoBrightness = autoBrightness;
+ displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
+ displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
- updatePowerRequestFromBatterySaverPolicy(displayPowerRequest);
+ updatePowerRequestFromBatterySaverPolicy(displayPowerRequest);
- if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
- displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
- if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0
- && !mDrawWakeLockOverrideFromSidekick) {
- if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
- displayPowerRequest.dozeScreenState = Display.STATE_DOZE;
+ if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
+ displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
+ if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0
+ && !mDrawWakeLockOverrideFromSidekick) {
+ if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
+ displayPowerRequest.dozeScreenState = Display.STATE_DOZE;
+ }
+ if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {
+ displayPowerRequest.dozeScreenState = Display.STATE_ON;
+ }
}
- if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {
- displayPowerRequest.dozeScreenState = Display.STATE_ON;
+ displayPowerRequest.dozeScreenBrightness =
+ mDozeScreenBrightnessOverrideFromDreamManagerFloat;
+ } else {
+ displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
+ displayPowerRequest.dozeScreenBrightness =
+ PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
+
+ final boolean ready = mDisplayManagerInternal.requestPowerState(groupId,
+ displayPowerRequest, mRequestWaitForNegativeProximity);
+
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready
+ + ", groupId=" + groupId
+ + ", policy=" + policyToString(displayPowerRequest.policy)
+ + ", mWakefulness="
+ + PowerManagerInternal.wakefulnessToString(
+ mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId))
+ + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
+ + ", mUserActivitySummary=0x" + Integer.toHexString(
+ mUserActivitySummary)
+ + ", mBootCompleted=" + mBootCompleted
+ + ", screenBrightnessOverride="
+ + displayPowerRequest.screenBrightnessOverride
+ + ", useAutoBrightness=" + displayPowerRequest.useAutoBrightness
+ + ", mScreenBrightnessBoostInProgress="
+ + mScreenBrightnessBoostInProgress
+ + ", mIsVrModeEnabled= " + mIsVrModeEnabled
+ + ", sQuiescent=" + sQuiescent);
+ }
+
+ final boolean displayReadyStateChanged =
+ mDisplayGroupPowerStateMapper.setDisplayGroupReadyLocked(groupId, ready);
+ if (ready && displayReadyStateChanged
+ && mDisplayGroupPowerStateMapper.getWakefulnessLocked(
+ groupId) == WAKEFULNESS_AWAKE) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
+ final int latencyMs = (int) (mClock.uptimeMillis()
+ - mDisplayGroupPowerStateMapper.getLastPowerOnTimeLocked(groupId));
+ if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
+ Slog.w(TAG, "Screen on took " + latencyMs + " ms");
}
}
- displayPowerRequest.dozeScreenBrightness =
- mDozeScreenBrightnessOverrideFromDreamManagerFloat;
- } else {
- displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
- displayPowerRequest.dozeScreenBrightness =
- PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
-
- mDisplayReady = mDisplayManagerInternal.requestPowerState(Display.DEFAULT_DISPLAY_GROUP,
- displayPowerRequest, mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
-
- if (DEBUG_SPEW) {
- Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady
- + ", policy=" + displayPowerRequest.policy
- + ", mWakefulness=" + getWakefulnessLocked()
- + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
- + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
- + ", mBootCompleted=" + mBootCompleted
- + ", screenBrightnessOverride=" + screenBrightnessOverride
- + ", useAutoBrightness=" + autoBrightness
- + ", mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress
- + ", mIsVrModeEnabled= " + mIsVrModeEnabled
- + ", sQuiescent=" + sQuiescent);
- }
}
- return mDisplayReady && !oldDisplayReady;
+
+ return mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked() && !oldDisplayReady;
}
private void updateScreenBrightnessBoostLocked(int dirty) {
@@ -2944,12 +3134,11 @@
}
@VisibleForTesting
- int getDesiredScreenPolicyLocked() {
- if (getWakefulnessLocked() == WAKEFULNESS_ASLEEP || sQuiescent) {
+ int getDesiredScreenPolicyLocked(int groupId) {
+ final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+ if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
return DisplayPowerRequest.POLICY_OFF;
- }
-
- if (getWakefulnessLocked() == WAKEFULNESS_DOZING) {
+ } else if (wakefulness == WAKEFULNESS_DOZING) {
if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
return DisplayPowerRequest.POLICY_DOZE;
}
@@ -2979,7 +3168,6 @@
private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks =
new DisplayManagerInternal.DisplayPowerCallbacks() {
- private int mDisplayState = Display.STATE_UNKNOWN;
@Override
public void onStateChanged() {
@@ -3010,29 +3198,25 @@
}
@Override
- public void onDisplayStateChange(int state) {
+ public void onDisplayStateChange(boolean allInactive, boolean allOff) {
// This method is only needed to support legacy display blanking behavior
// where the display's power state is coupled to suspend or to the power HAL.
// The order of operations matters here.
synchronized (mLock) {
- if (mDisplayState != state) {
- mDisplayState = state;
- setPowerModeInternal(MODE_DISPLAY_INACTIVE,
- !Display.isActiveState(state));
- if (state == Display.STATE_OFF) {
- if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
- setHalInteractiveModeLocked(false);
- }
- if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
- setHalAutoSuspendModeLocked(true);
- }
- } else {
- if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
- setHalAutoSuspendModeLocked(false);
- }
- if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
- setHalInteractiveModeLocked(true);
- }
+ setPowerModeInternal(MODE_DISPLAY_INACTIVE, allInactive);
+ if (allOff) {
+ if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
+ setHalInteractiveModeLocked(false);
+ }
+ if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
+ setHalAutoSuspendModeLocked(true);
+ }
+ } else {
+ if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
+ setHalAutoSuspendModeLocked(false);
+ }
+ if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
+ setHalInteractiveModeLocked(true);
}
}
}
@@ -3047,13 +3231,6 @@
public void releaseSuspendBlocker() {
mDisplaySuspendBlocker.release();
}
-
- @Override
- public String toString() {
- synchronized (this) {
- return "state=" + Display.stateToString(mDisplayState);
- }
- }
};
private boolean shouldUseProximitySensorLocked() {
@@ -3069,9 +3246,11 @@
final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
final boolean autoSuspend = !needDisplaySuspendBlocker;
- final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
- Display.DEFAULT_DISPLAY);
- final boolean interactive = displayPowerRequest.isBrightOrDim();
+ final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked();
+ boolean interactive = false;
+ for (int id : groupIds) {
+ interactive |= mDisplayGroupPowerStateMapper.getPowerRequestLocked(id).isBrightOrDim();
+ }
// Disable auto-suspend if needed.
// FIXME We should consider just leaving auto-suspend enabled forever since
@@ -3101,7 +3280,7 @@
// until the display is actually ready so that all transitions have
// completed. This is probably a good sign that things have gotten
// too tangled over here...
- if (interactive || mDisplayReady) {
+ if (interactive || mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
setHalInteractiveModeLocked(interactive);
}
}
@@ -3127,29 +3306,10 @@
* We do so if the screen is on or is in transition between states.
*/
private boolean needDisplaySuspendBlockerLocked() {
- if (!mDisplayReady) {
+ if (!mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
return true;
}
- final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
- Display.DEFAULT_DISPLAY);
- if (displayPowerRequest.isBrightOrDim()) {
- // If we asked for the screen to be on but it is off due to the proximity
- // sensor then we may suspend but only if the configuration allows it.
- // On some hardware it may not be safe to suspend because the proximity
- // sensor may not be correctly configured as a wake-up source.
- if (!displayPowerRequest.useProximitySensor || !mProximityPositive
- || !mSuspendWhenScreenOffDueToProximityConfig) {
- return true;
- }
- }
- if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE
- && displayPowerRequest.dozeScreenState == Display.STATE_ON) {
- // Although we are in DOZE and would normally allow the device to suspend,
- // the doze service has explicitly requested the display to remain in the ON
- // state which means we should hold the display suspend blocker.
- return true;
- }
if (mScreenBrightnessBoostInProgress) {
return true;
}
@@ -3163,6 +3323,30 @@
return true;
}
+ final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked();
+ for (int id : groupIds) {
+ final DisplayPowerRequest displayPowerRequest =
+ mDisplayGroupPowerStateMapper.getPowerRequestLocked(id);
+ if (displayPowerRequest.isBrightOrDim()) {
+ // If we asked for the screen to be on but it is off due to the proximity
+ // sensor then we may suspend but only if the configuration allows it.
+ // On some hardware it may not be safe to suspend because the proximity
+ // sensor may not be correctly configured as a wake-up source.
+ if (!displayPowerRequest.useProximitySensor || !mProximityPositive
+ || !mSuspendWhenScreenOffDueToProximityConfig) {
+ return true;
+ }
+ }
+
+ if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE
+ && displayPowerRequest.dozeScreenState == Display.STATE_ON) {
+ // Although we are in DOZE and would normally allow the device to suspend,
+ // the doze service has explicitly requested the display to remain in the ON
+ // state which means we should hold the display suspend blocker.
+ return true;
+ }
+ }
+
// Let the system suspend if the screen is off or dozing.
return false;
}
@@ -3696,9 +3880,15 @@
synchronized (mLock) {
mForceSuspendActive = true;
// Place the system in an non-interactive state
- goToSleepInternal(mClock.uptimeMillis(),
- PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
- PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);
+ boolean updatePowerState = false;
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ updatePowerState |= sleepDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);
+ }
+ if (updatePowerState) {
+ updatePowerStateLocked();
+ }
// Disable all the partial wake locks as well
updateWakeLockDisabledStatesLocked();
@@ -3838,7 +4028,6 @@
pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
pw.println(" mSandmanScheduled=" + mSandmanScheduled);
- pw.println(" mSandmanSummoned=" + mSandmanSummoned);
pw.println(" mBatteryLevelLow=" + mBatteryLevelLow);
pw.println(" mLightDeviceIdleMode=" + mLightDeviceIdleMode);
pw.println(" mDeviceIdleMode=" + mDeviceIdleMode);
@@ -3856,9 +4045,10 @@
+ TimeUtils.formatUptime(mLastScreenBrightnessBoostTime));
pw.println(" mScreenBrightnessBoostInProgress="
+ mScreenBrightnessBoostInProgress);
- pw.println(" mDisplayReady=" + mDisplayReady);
pw.println(" mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker);
pw.println(" mHoldingDisplaySuspendBlocker=" + mHoldingDisplaySuspendBlocker);
+ pw.println(" mLastFlipTime=" + mLastFlipTime);
+ pw.println(" mIsFaceDown=" + mIsFaceDown);
pw.println();
pw.println("Settings and Configuration:");
@@ -4003,6 +4193,8 @@
mNotifier.dump(pw);
}
+ mFaceDownDetector.dump(pw);
+
mAmbientDisplaySuppressionController.dump(pw);
}
@@ -4091,7 +4283,6 @@
PowerManagerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY,
mRequestWaitForNegativeProximity);
proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled);
- proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned);
proto.write(PowerManagerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow);
proto.write(PowerManagerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode);
proto.write(PowerManagerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode);
@@ -4118,7 +4309,6 @@
proto.write(
PowerManagerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS,
mScreenBrightnessBoostInProgress);
- proto.write(PowerManagerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady);
proto.write(
PowerManagerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER,
mHoldingWakeLockSuspendBlocker);
@@ -4932,7 +5122,8 @@
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- wakeUpInternal(eventTime, reason, details, uid, opPackageName, uid);
+ wakeDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, uid,
+ opPackageName, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4950,7 +5141,7 @@
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- goToSleepInternal(eventTime, reason, flags, uid);
+ sleepDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, flags, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4968,7 +5159,7 @@
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- napInternal(eventTime, uid);
+ dreamDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5121,7 +5312,6 @@
@Override // Binder call
public int getPowerSaveModeTrigger() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER, null);
final long ident = Binder.clearCallingIdentity();
try {
return Settings.Global.getInt(mContext.getContentResolver(),
@@ -5631,7 +5821,8 @@
// also tells us that we're not already ignoring the proximity sensor.
final DisplayPowerRequest displayPowerRequest =
- mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY);
+ mDisplayGroupPowerStateMapper.getPowerRequestLocked(
+ Display.DEFAULT_DISPLAY_GROUP);
if (displayPowerRequest.useProximitySensor && mProximityPositive) {
mDisplayManagerInternal.ignoreProximitySensorUntilChanged();
return true;
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 6f7c016..0cd0458 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -65,6 +65,7 @@
@GuardedBy("mLock")
RemoteRotationResolverService mRemoteService;
+ private static String sTestingPackage;
private ComponentName mComponentName;
RotationResolverManagerPerUserService(@NonNull RotationResolverManagerService main,
@@ -136,19 +137,43 @@
}
/**
+ * Set the testing package name.
+ *
+ * @param packageName the name of the package that implements {@link RotationResolverService}
+ * and is used for testing only.
+ */
+ @VisibleForTesting
+ void setTestingPackage(String packageName) {
+ sTestingPackage = packageName;
+ mComponentName = resolveRotationResolverService(getContext());
+ }
+
+ /**
+ * get the currently bound component name.
+ */
+ @VisibleForTesting
+ ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
* Provides rotation resolver service component name at runtime, making sure it's provided
* by the system.
*/
- private static ComponentName resolveRotationResolverService(Context context) {
- final String serviceConfigPackage = getServiceConfigPackage(context);
-
+ static ComponentName resolveRotationResolverService(Context context) {
String resolvedPackage;
int flags = PackageManager.MATCH_SYSTEM_ONLY;
-
- if (!TextUtils.isEmpty(serviceConfigPackage)) {
- resolvedPackage = serviceConfigPackage;
+ if (!TextUtils.isEmpty(sTestingPackage)) {
+ // Testing Package is set.
+ resolvedPackage = sTestingPackage;
+ flags = PackageManager.GET_META_DATA;
} else {
- return null;
+ final String serviceConfigPackage = getServiceConfigPackage(context);
+ if (!TextUtils.isEmpty(serviceConfigPackage)) {
+ resolvedPackage = serviceConfigPackage;
+ } else {
+ return null;
+ }
}
final Intent intent = new Intent(
@@ -158,14 +183,15 @@
flags, context.getUserId());
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
Slog.wtf(TAG, String.format("Service %s not found in package %s",
- RotationResolverService.SERVICE_INTERFACE, serviceConfigPackage
- ));
+ RotationResolverService.SERVICE_INTERFACE, resolvedPackage));
return null;
}
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
final String permission = serviceInfo.permission;
if (Manifest.permission.BIND_ROTATION_RESOLVER_SERVICE.equals(permission)) {
+ Slog.i(TAG, String.format("Successfully bound the service from package: %s",
+ resolvedPackage));
return serviceInfo.getComponentName();
}
Slog.e(TAG, String.format(
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
index 54a9edb..e5088c0 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
@@ -16,12 +16,13 @@
package com.android.server.rotationresolver;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.os.CancellationSignal;
import android.os.ShellCommand;
import android.rotationresolver.RotationResolverInternal.RotationResolverCallbackInternal;
+import android.text.TextUtils;
import android.view.Surface;
import java.io.PrintWriter;
@@ -59,7 +60,7 @@
}
}
- final TestableRotationCallbackInternal mTestableRotationCallbackInternal =
+ static final TestableRotationCallbackInternal sTestableRotationCallbackInternal =
new TestableRotationCallbackInternal();
@Override
@@ -73,20 +74,47 @@
return runResolveRotation();
case "get-last-resolution":
return getLastResolution();
+ case "set-testing-package":
+ return setTestRotationResolverPackage(getNextArgRequired());
+ case "get-bound-package":
+ return getBoundPackageName();
+ case "clear-testing-package":
+ return resetTestRotationResolverPackage();
default:
return handleDefaultCommands(cmd);
}
}
+ private int getBoundPackageName() {
+ final PrintWriter out = getOutPrintWriter();
+ final ComponentName componentName = mService.getComponentName();
+ out.println(componentName == null ? "" : componentName.getPackageName());
+ return 0;
+ }
+
+ private int setTestRotationResolverPackage(String testingPackage) {
+ if (!TextUtils.isEmpty((testingPackage))) {
+ mService.setTestingPackage(testingPackage);
+ sTestableRotationCallbackInternal.reset();
+ }
+ return 0;
+ }
+
+ private int resetTestRotationResolverPackage() {
+ mService.setTestingPackage("");
+ sTestableRotationCallbackInternal.reset();
+ return 0;
+ }
+
private int runResolveRotation() {
- mService.resolveRotationLocked(mTestableRotationCallbackInternal, Surface.ROTATION_0,
+ mService.resolveRotationLocked(sTestableRotationCallbackInternal, Surface.ROTATION_0,
Surface.ROTATION_0, "", 2000L, new CancellationSignal());
return 0;
}
private int getLastResolution() {
final PrintWriter out = getOutPrintWriter();
- out.println(mTestableRotationCallbackInternal.getLastCallbackCode());
+ out.println(sTestableRotationCallbackInternal.getLastCallbackCode());
return 0;
}
@@ -99,5 +127,8 @@
pw.println();
pw.println(" resolve-rotation: request a rotation resolution.");
pw.println(" get-last-resolution: show the last rotation resolution result.");
+ pw.println(" set-testing-package: Set the testing package that implements the service.");
+ pw.println(" get-bound-package: print the bound package that implements the service.");
+ pw.println(" clear-testing-package: reset the testing package.");
}
}
diff --git a/services/core/java/com/android/server/speech/Android.bp b/services/core/java/com/android/server/speech/Android.bp
index 379b075..5605349 100644
--- a/services/core/java/com/android/server/speech/Android.bp
+++ b/services/core/java/com/android/server/speech/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.speech-sources",
srcs: ["java/**/*.java"],
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 342dbfa..6aa7c8a 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1471,12 +1471,18 @@
}
int pullCpuTimePerClusterFreqLocked(int atomTag, List<StatsEvent> pulledData) {
- boolean success = KernelCpuTotalBpfMapReader.read((cluster, freq, timeMs) -> {
- pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs));
- });
- if (!success) {
+ int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters();
+ long[] freqs = KernelCpuBpfTracking.getFreqs();
+ long[] timesMs = KernelCpuTotalBpfMapReader.read();
+ if (timesMs == null) {
return StatsManager.PULL_SKIP;
}
+ for (int freqIndex = 0; freqIndex < timesMs.length; ++freqIndex) {
+ int cluster = freqsClusters[freqIndex];
+ long freq = freqs[freqIndex];
+ long timeMs = timesMs[freqIndex];
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs));
+ }
return StatsManager.PULL_SUCCESS;
}
@@ -1503,48 +1509,42 @@
}
private void registerCpuCyclesPerUidCluster() {
- int tagId = FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER;
- PullAtomMetadata metadata = new PullAtomMetadata.Builder()
- .setAdditiveFields(new int[] {3, 4, 5})
- .build();
- mStatsManager.setPullAtomCallback(
- tagId,
- metadata,
- DIRECT_EXECUTOR,
- mStatsCallbackImpl
- );
+ // If eBPF tracking is not support, the procfs fallback is used if the kernel knows about
+ // CPU frequencies.
+ if (KernelCpuBpfTracking.isSupported() || KernelCpuBpfTracking.getClusters() > 0) {
+ int tagId = FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER;
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
+ .setAdditiveFields(new int[] {3, 4, 5})
+ .build();
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ metadata,
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
}
int pullCpuCyclesPerUidClusterLocked(int atomTag, List<StatsEvent> pulledData) {
- // TODO(b/179485697): Remove power profile dependency.
PowerProfile powerProfile = new PowerProfile(mContext);
- // Frequency index to frequency mapping.
- long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile);
- // Frequency index to cluster mapping.
- int[] freqClusters = new int[freqs.length];
- // Frequency index to power mapping.
- double[] freqPowers = new double[freqs.length];
- // Number of clusters.
- int clusters;
-
- // Initialize frequency mappings.
+ int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters();
+ int clusters = KernelCpuBpfTracking.getClusters();
+ long[] freqs = KernelCpuBpfTracking.getFreqs();
+ double[] freqsPowers = new double[freqs.length];
+ // Initialize frequency power mapping.
{
- int cluster = 0;
int freqClusterIndex = 0;
- long lastFreq = -1;
+ int lastCluster = -1;
for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex, ++freqClusterIndex) {
- long currFreq = freqs[freqIndex];
- if (currFreq <= lastFreq) {
- cluster++;
+ int cluster = freqsClusters[freqIndex];
+ if (cluster != lastCluster) {
freqClusterIndex = 0;
}
- freqClusters[freqIndex] = cluster;
- freqPowers[freqIndex] =
- powerProfile.getAveragePowerForCpuCore(cluster, freqClusterIndex);
- lastFreq = currFreq;
- }
+ lastCluster = cluster;
- clusters = cluster + 1;
+ freqsPowers[freqIndex] =
+ powerProfile.getAveragePowerForCpuCore(cluster, freqClusterIndex);
+ }
}
// Aggregate 0: mcycles, 1: runtime ms, 2: power profile estimate for the same uids for
@@ -1570,12 +1570,12 @@
}
for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
- int cluster = freqClusters[freqIndex];
+ int cluster = freqsClusters[freqIndex];
long timeMs = cpuFreqTimeMs[freqIndex];
values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES] += freqs[freqIndex] * timeMs;
values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 1] += timeMs;
values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 2] +=
- freqPowers[freqIndex] * timeMs;
+ freqsPowers[freqIndex] * timeMs;
}
});
@@ -1665,34 +1665,6 @@
}
int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) {
- // TODO(b/179485697): Remove power profile dependency.
- PowerProfile powerProfile = new PowerProfile(mContext);
- // Frequency index to frequency mapping.
- long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile);
- if (freqs == null) {
- return StatsManager.PULL_SKIP;
- }
- // Frequency index to cluster mapping.
- int[] freqClusters = new int[freqs.length];
- // Number of clusters.
- int clusters;
-
- // Initialize frequency mappings.
- {
- int cluster = 0;
- long lastFreq = -1;
- for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex) {
- long currFreq = freqs[freqIndex];
- if (currFreq <= lastFreq) {
- cluster++;
- }
- freqClusters[freqIndex] = cluster;
- lastFreq = currFreq;
- }
-
- clusters = cluster + 1;
- }
-
SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class)
.getSystemServiceCpuThreadTimes();
if (times == null) {
@@ -1701,22 +1673,24 @@
addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData,
FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER,
- times.threadCpuTimesUs, clusters, freqs, freqClusters);
+ times.threadCpuTimesUs);
addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData,
FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER,
- times.binderThreadCpuTimesUs, clusters, freqs, freqClusters);
+ times.binderThreadCpuTimesUs);
return StatsManager.PULL_SUCCESS;
}
private static void addCpuCyclesPerThreadGroupClusterAtoms(
- int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs,
- int clusters, long[] freqs, int[] freqClusters) {
+ int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs) {
+ int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters();
+ int clusters = KernelCpuBpfTracking.getClusters();
+ long[] freqs = KernelCpuBpfTracking.getFreqs();
long[] aggregatedCycles = new long[clusters];
long[] aggregatedTimesUs = new long[clusters];
for (int i = 0; i < cpuTimesUs.length; ++i) {
- aggregatedCycles[freqClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000;
- aggregatedTimesUs[freqClusters[i]] += cpuTimesUs[i];
+ aggregatedCycles[freqsClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000;
+ aggregatedTimesUs[freqsClusters[i]] += cpuTimesUs[i];
}
for (int cluster = 0; cluster < clusters; ++cluster) {
pulledData.add(FrameworkStatsLog.buildStatsEvent(
diff --git a/services/core/java/com/android/server/utils/WatchedLongSparseArray.java b/services/core/java/com/android/server/utils/WatchedLongSparseArray.java
new file mode 100644
index 0000000..bf23de1
--- /dev/null
+++ b/services/core/java/com/android/server/utils/WatchedLongSparseArray.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.LongSparseArray;
+
+/**
+ * A watched variant of {@link LongSparseArray}. If a {@link Watchable} is stored in the
+ * array, the array registers with the {@link Watchable}. The array registers only once
+ * with each {@link Watchable} no matter how many times the {@link Watchable} is stored in
+ * the array.
+ * @param <E> The element type, stored in the array.
+ */
+public class WatchedLongSparseArray<E> extends WatchableImpl
+ implements Snappable {
+
+ // The storage
+ private final LongSparseArray<E> mStorage;
+
+ // If true, the array is watching its children
+ private volatile boolean mWatching = false;
+
+ // The local observer
+ private final Watcher mObserver = new Watcher() {
+ @Override
+ public void onChange(@Nullable Watchable o) {
+ WatchedLongSparseArray.this.dispatchChange(o);
+ }
+ };
+
+ /**
+ * A private convenience function that notifies registered listeners that an element
+ * has been added to or removed from the array. The what parameter is the array itself.
+ */
+ private void onChanged() {
+ dispatchChange(this);
+ }
+
+ /**
+ * A convenience function. Register the object if it is {@link Watchable} and if the
+ * array is currently watching. Note that the watching flag must be true if this
+ * function is to succeed.
+ */
+ private void registerChild(Object o) {
+ if (mWatching && o instanceof Watchable) {
+ ((Watchable) o).registerObserver(mObserver);
+ }
+ }
+
+ /**
+ * A convenience function. Unregister the object if it is {@link Watchable} and if
+ * the array is currently watching. Note that the watching flag must be true if this
+ * function is to succeed.
+ */
+ private void unregisterChild(Object o) {
+ if (mWatching && o instanceof Watchable) {
+ ((Watchable) o).unregisterObserver(mObserver);
+ }
+ }
+
+ /**
+ * A convenience function. Unregister the object if it is {@link Watchable}, if the array is
+ * currently watching, and if the storage does not contain the object. Note that the watching
+ * flag must be true if this function is to succeed. This must be called after an object has
+ * been removed from the storage.
+ */
+ private void unregisterChildIf(Object o) {
+ if (mWatching && o instanceof Watchable) {
+ if (mStorage.indexOfValue((E) o) == -1) {
+ ((Watchable) o).unregisterObserver(mObserver);
+ }
+ }
+ }
+
+ /**
+ * Register a {@link Watcher} with the array. If this is the first Watcher than any
+ * array values that are {@link Watchable} are registered to the array itself.
+ */
+ @Override
+ public void registerObserver(@NonNull Watcher observer) {
+ super.registerObserver(observer);
+ if (registeredObserverCount() == 1) {
+ // The watching flag must be set true before any children are registered.
+ mWatching = true;
+ final int end = mStorage.size();
+ for (int i = 0; i < end; i++) {
+ registerChild(mStorage.valueAt(i));
+ }
+ }
+ }
+
+ /**
+ * Unregister a {@link Watcher} from the array. If this is the last Watcher than any
+ * array values that are {@link Watchable} are unregistered to the array itself.
+ */
+ @Override
+ public void unregisterObserver(@NonNull Watcher observer) {
+ super.unregisterObserver(observer);
+ if (registeredObserverCount() == 0) {
+ final int end = mStorage.size();
+ for (int i = 0; i < end; i++) {
+ unregisterChild(mStorage.valueAt(i));
+ }
+ // The watching flag must be true while children are unregistered.
+ mWatching = false;
+ }
+ }
+
+ /**
+ * Creates a new WatchedSparseArray containing no mappings.
+ */
+ public WatchedLongSparseArray() {
+ mStorage = new LongSparseArray();
+ }
+
+ /**
+ * Creates a new WatchedSparseArray containing no mappings that
+ * will not require any additional memory allocation to store the
+ * specified number of mappings. If you supply an initial capacity of
+ * 0, the sparse array will be initialized with a light-weight
+ * representation not requiring any additional array allocations.
+ */
+ public WatchedLongSparseArray(int initialCapacity) {
+ mStorage = new LongSparseArray(initialCapacity);
+ }
+
+ /**
+ * Create a {@link WatchedLongSparseArray} from a {@link LongSparseArray}.
+ */
+ public WatchedLongSparseArray(@NonNull LongSparseArray<E> c) {
+ mStorage = c.clone();
+ }
+
+ /**
+ * The copy constructor does not copy the watcher data.
+ */
+ public WatchedLongSparseArray(@NonNull WatchedLongSparseArray<E> r) {
+ mStorage = r.mStorage.clone();
+ }
+
+ /**
+ * Make <this> a copy of src. Any data in <this> is discarded.
+ */
+ public void copyFrom(@NonNull LongSparseArray<E> src) {
+ clear();
+ final int end = src.size();
+ for (int i = 0; i < end; i++) {
+ put(src.keyAt(i), src.valueAt(i));
+ }
+ }
+
+ /**
+ * Make dst a copy of <this>. Any previous data in dst is discarded.
+ */
+ public void copyTo(@NonNull LongSparseArray<E> dst) {
+ dst.clear();
+ final int end = size();
+ for (int i = 0; i < end; i++) {
+ dst.put(keyAt(i), valueAt(i));
+ }
+ }
+
+ /**
+ * Return the underlying storage. This breaks the wrapper but is necessary when
+ * passing the array to distant methods.
+ */
+ public LongSparseArray<E> untrackedStorage() {
+ return mStorage;
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or <code>null</code>
+ * if no such mapping has been made.
+ */
+ public E get(long key) {
+ return mStorage.get(key);
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or the specified Object
+ * if no such mapping has been made.
+ */
+ public E get(long key, E valueIfKeyNotFound) {
+ return mStorage.get(key, valueIfKeyNotFound);
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(long key) {
+ E old = mStorage.get(key, null);
+ mStorage.delete(key);
+ unregisterChildIf(old);
+ onChanged();
+
+ }
+
+ /**
+ * Alias for {@link #delete(long)}.
+ */
+ public void remove(long key) {
+ delete(key);
+ }
+
+ /**
+ * Removes the mapping at the specified index.
+ *
+ * <p>For indices outside of the range <code>0...size()-1</code>, an
+ * {@link ArrayIndexOutOfBoundsException} is thrown.</p>
+ */
+ public void removeAt(int index) {
+ E old = mStorage.valueAt(index);
+ mStorage.removeAt(index);
+ unregisterChildIf(old);
+ onChanged();
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(long key, E value) {
+ E old = mStorage.get(key);
+ mStorage.put(key, value);
+ unregisterChildIf(old);
+ registerChild(value);
+ onChanged();
+ }
+
+ /**
+ * Returns the number of key-value mappings that this LongSparseArray
+ * currently stores.
+ */
+ public int size() {
+ return mStorage.size();
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the key from the <code>index</code>th key-value mapping that this
+ * LongSparseArray stores.
+ *
+ * <p>The keys corresponding to indices in ascending order are guaranteed to
+ * be in ascending order, e.g., <code>keyAt(0)</code> will return the
+ * smallest key and <code>keyAt(size()-1)</code> will return the largest
+ * key.</p>
+ *
+ * <p>For indices outside of the range <code>0...size()-1</code>, an
+ * {@link ArrayIndexOutOfBoundsException} is thrown.</p>
+ */
+ public long keyAt(int index) {
+ return mStorage.keyAt(index);
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the value from the <code>index</code>th key-value mapping that this
+ * LongSparseArray stores.
+ *
+ * <p>The values corresponding to indices in ascending order are guaranteed
+ * to be associated with keys in ascending order, e.g.,
+ * <code>valueAt(0)</code> will return the value associated with the
+ * smallest key and <code>valueAt(size()-1)</code> will return the value
+ * associated with the largest key.</p>
+ *
+ * <p>For indices outside of the range <code>0...size()-1</code>, an
+ * {@link ArrayIndexOutOfBoundsException} is thrown.</p>
+ */
+ public E valueAt(int index) {
+ return mStorage.valueAt(index);
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, sets a new
+ * value for the <code>index</code>th key-value mapping that this
+ * LongSparseArray stores.
+ *
+ * <p>For indices outside of the range <code>0...size()-1</code>, an
+ * {@link ArrayIndexOutOfBoundsException} is thrown.</p>
+ */
+ public void setValueAt(int index, E value) {
+ E old = mStorage.valueAt(index);
+ mStorage.setValueAt(index, value);
+ unregisterChildIf(old);
+ registerChild(value);
+ onChanged();
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(long key) {
+ return mStorage.indexOfKey(key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(E value) {
+ return mStorage.indexOfValue(value);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * <p>Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ * <p>Note also that this method uses {@code equals} unlike {@code indexOfValue}.
+ * @hide
+ */
+ public int indexOfValueByValue(E value) {
+ return mStorage.indexOfValueByValue(value);
+ }
+
+ /**
+ * Removes all key-value mappings from this LongSparseArray.
+ */
+ public void clear() {
+ final int end = mStorage.size();
+ for (int i = 0; i < end; i++) {
+ unregisterChild(mStorage.valueAt(i));
+ }
+ mStorage.clear();
+ onChanged();
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(long key, E value) {
+ mStorage.append(key, value);
+ registerChild(value);
+ onChanged();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation composes a string by iterating over its mappings. If
+ * this map contains itself as a value, the string "(this Map)"
+ * will appear in its place.
+ */
+ @Override
+ public String toString() {
+ return mStorage.toString();
+ }
+
+ /**
+ * Create a copy of the array. If the element is a subclass of Snapper then the copy
+ * contains snapshots of the elements. Otherwise the copy contains references to the
+ * elements. The returned snapshot is immutable.
+ * @return A new array whose elements are the elements of <this>.
+ */
+ public WatchedLongSparseArray<E> snapshot() {
+ WatchedLongSparseArray<E> l = new WatchedLongSparseArray<>(size());
+ snapshot(l, this);
+ return l;
+ }
+
+ /**
+ * Make <this> a snapshot of the argument. Note that <this> is immutable when the
+ * method returns. <this> must be empty when the function is called.
+ * @param r The source array, which is copied into <this>
+ */
+ public void snapshot(@NonNull WatchedLongSparseArray<E> r) {
+ snapshot(this, r);
+ }
+
+ /**
+ * Make the destination a copy of the source. If the element is a subclass of Snapper then the
+ * copy contains snapshots of the elements. Otherwise the copy contains references to the
+ * elements. The destination must be initially empty. Upon return, the destination is
+ * immutable.
+ * @param dst The destination array. It must be empty.
+ * @param src The source array. It is not modified.
+ */
+ public static <E> void snapshot(@NonNull WatchedLongSparseArray<E> dst,
+ @NonNull WatchedLongSparseArray<E> src) {
+ if (dst.size() != 0) {
+ throw new IllegalArgumentException("snapshot destination is not empty");
+ }
+ final int end = src.size();
+ for (int i = 0; i < end; i++) {
+ final E val = Snapshots.maybeSnapshot(src.valueAt(i));
+ final long key = src.keyAt(i);
+ dst.put(key, val);
+ }
+ dst.seal();
+ }
+
+}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 9ee072e..06748a3 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -969,7 +969,7 @@
mGatewayStatusCallback.onGatewayConnectionError(
mConnectionConfig.getRequiredUnderlyingCapabilities(),
VCN_ERROR_CODE_INTERNAL_ERROR,
- "java.lang.RuntimeException",
+ RuntimeException.class.getName(),
"Received "
+ exception.getClass().getSimpleName()
+ " with message: "
@@ -991,11 +991,11 @@
} else if (exception instanceof IkeInternalException
&& exception.getCause() instanceof IOException) {
errorCode = VCN_ERROR_CODE_NETWORK_ERROR;
- exceptionClass = "java.io.IOException";
+ exceptionClass = IOException.class.getName();
exceptionMessage = exception.getCause().getMessage();
} else {
errorCode = VCN_ERROR_CODE_INTERNAL_ERROR;
- exceptionClass = "java.lang.RuntimeException";
+ exceptionClass = RuntimeException.class.getName();
exceptionMessage =
"Received "
+ exception.getClass().getSimpleName()
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 904bbe8..151895f 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -235,7 +235,10 @@
public void activityRelaunched(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
- mTaskSupervisor.activityRelaunchedLocked(token);
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r != null) {
+ r.finishRelaunching();
+ }
}
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8c3722d..5d3b9c1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -567,6 +567,7 @@
boolean mVoiceInteraction;
private int mPendingRelaunchCount;
+ long mRelaunchStartTime;
// True if we are current in the process of removing this app token from the display
private boolean mRemovingFromDisplay = false;
@@ -3357,7 +3358,11 @@
return task.isDragResizing();
}
+ @VisibleForTesting
void startRelaunching() {
+ if (mPendingRelaunchCount == 0) {
+ mRelaunchStartTime = SystemClock.elapsedRealtime();
+ }
if (shouldFreezeBounds()) {
freezeBounds();
}
@@ -3396,6 +3401,14 @@
// Update keyguard flags upon finishing relaunch.
checkKeyguardFlagsChanged();
}
+
+ final Task rootTask = getRootTask();
+ if (rootTask != null && rootTask.shouldSleepOrShutDownActivities()) {
+ // Activity is always relaunched to either resumed or paused state. If it was
+ // relaunched while hidden (by keyguard or smth else), it should be stopped.
+ rootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */);
+ }
}
void clearRelaunching() {
@@ -3404,6 +3417,7 @@
}
unfreezeBounds();
mPendingRelaunchCount = 0;
+ mRelaunchStartTime = 0;
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 75a7660..b803fc3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4015,8 +4015,7 @@
deferWindowLayout();
try {
if (values != null) {
- changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
- deferResume);
+ changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId);
}
if (!deferResume) {
@@ -4035,7 +4034,7 @@
/** Update default (global) configuration and notify listeners about changes. */
int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
- boolean persistent, int userId, boolean deferResume) {
+ boolean persistent, int userId) {
final DisplayContent defaultDisplay =
mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
@@ -4047,7 +4046,7 @@
// setting WindowManagerService.mWaitingForConfig to true, it is important that we call
// performDisplayOverrideConfigUpdate in order to send the new display configuration
// (even if there are no actual changes) to unfreeze the window.
- defaultDisplay.performDisplayOverrideConfigUpdate(values, deferResume);
+ defaultDisplay.performDisplayOverrideConfigUpdate(values);
return 0;
}
@@ -4095,9 +4094,6 @@
mTempConfig.seq = increaseConfigurationSeqLocked();
- // Update stored global config and notify everyone about the change.
- mRootWindowContainer.onConfigurationChanged(mTempConfig);
-
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
// TODO(multi-display): Update UsageEvents#Event to include displayId.
mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());
@@ -4116,13 +4112,10 @@
// resources have that config before following boot code is executed.
mSystemThread.applyConfigurationToResources(mTempConfig);
- // We need another copy of global config because we're scheduling some calls instead of
- // running them in place. We need to be sure that object we send will be handled unchanged.
- final Configuration configCopy = new Configuration(mTempConfig);
if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
final Message msg = PooledLambda.obtainMessage(
ActivityTaskManagerService::sendPutConfigurationForUserMsg,
- this, userId, configCopy);
+ this, userId, new Configuration(mTempConfig));
mH.sendMessage(msg);
}
@@ -4131,8 +4124,8 @@
final int pid = pidMap.keyAt(i);
final WindowProcessController app = pidMap.get(pid);
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new "
- + "config %s", app.mName, configCopy);
- app.onConfigurationChanged(configCopy);
+ + "config %s", app.mName, mTempConfig);
+ app.onConfigurationChanged(mTempConfig);
}
final Message msg = PooledLambda.obtainMessage(
@@ -4140,10 +4133,8 @@
mAmInternal, changes, initLocale);
mH.sendMessage(msg);
- // Override configuration of the default display duplicates global config, so we need to
- // update it also. This will also notify WindowManager about changes.
- defaultDisplay.performDisplayOverrideConfigUpdate(mRootWindowContainer.getConfiguration(),
- deferResume);
+ // Update stored global config and notify everyone about the change.
+ mRootWindowContainer.onConfigurationChanged(mTempConfig);
return changes;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 1264d0c..bf2aae8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2204,19 +2204,6 @@
task.mTaskId, reason, topActivity.info.applicationInfo.packageName);
}
- void activityRelaunchedLocked(IBinder token) {
- final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
- if (r != null) {
- r.finishRelaunching();
- if (r.getRootTask().shouldSleepOrShutDownActivities()) {
- // Activity is always relaunched to either resumed or paused state. If it was
- // relaunched while hidden (by keyguard or smth else), it should be stopped.
- r.getRootTask().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
- }
- }
- }
-
void logRootTaskState() {
mActivityMetricsLogger.logWindowState();
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index efcaaa4..309b5ec 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -141,6 +141,10 @@
mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
mMergedOverrideConfiguration);
}
+ dispatchConfigurationToChildren();
+ }
+
+ void dispatchConfigurationToChildren() {
for (int i = getChildCount() - 1; i >= 0; --i) {
final ConfigurationContainer child = getChildAt(i);
child.onConfigurationChanged(mFullConfiguration);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 36d76f0..0143e70 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -673,10 +673,6 @@
// Used in updating override configurations
private final Configuration mTempConfig = new Configuration();
- // Used in performing layout, to record the insets provided by other windows above the current
- // window.
- private InsetsState mTmpAboveInsetsState = new InsetsState();
-
/**
* Used to prevent recursions when calling
* {@link #ensureActivitiesVisible(ActivityRecord, int, boolean, boolean)}
@@ -778,13 +774,6 @@
+ " parentHidden=" + w.isParentWindowHidden());
}
- // Sets mAboveInsets for each window. Windows behind the window providing the insets can
- // receive the insets.
- if (!w.mAboveInsetsState.equals(mTmpAboveInsetsState)) {
- w.mAboveInsetsState.set(mTmpAboveInsetsState);
- mWinInsetsChanged.add(w);
- }
-
// If this view is GONE, then skip it -- keep the current frame, and let the caller know
// so they can ignore it if they want. (We do the normal layout for INVISIBLE windows,
// since that means "perform layout as normal, just don't display").
@@ -818,16 +807,8 @@
+ " mContainingFrame=" + w.getContainingFrame()
+ " mDisplayFrame=" + w.getDisplayFrame());
}
- provideInsetsByWindow(w);
};
- private void provideInsetsByWindow(WindowState w) {
- for (int i = 0; i < w.mProvidedInsetsSources.size(); i++) {
- final InsetsSource providedSource = w.mProvidedInsetsSources.valueAt(i);
- mTmpAboveInsetsState.addSource(providedSource);
- }
- }
-
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
if (w.mLayoutAttached) {
if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame
@@ -2511,8 +2492,13 @@
void onDisplayInfoChanged() {
final DisplayInfo info = mDisplayInfo;
- mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation),
- calculateRoundedCornersForRotation(info.rotation));
+ if (mDisplayFrames.onDisplayInfoUpdated(info,
+ calculateDisplayCutoutForRotation(info.rotation),
+ calculateRoundedCornersForRotation(info.rotation))) {
+ // TODO(b/161810301): Set notifyInsetsChange to true while the server no longer performs
+ // layout.
+ mInsetsStateController.onDisplayInfoUpdated(false /* notifyInsetsChange */);
+ }
mInputMonitor.layoutInputConsumers(info.logicalWidth, info.logicalHeight);
mDisplayPolicy.onDisplayInfoChanged(info);
}
@@ -3767,8 +3753,11 @@
// 2. Assign window layers based on the IME surface parent to make sure it is on top of the
// app.
assignWindowLayers(true /* setLayoutNeeded */);
- // 3. Update the IME control target to apply any inset change and animation.
- // 4. Reparent the IME container surface to either the input target app, or the IME window
+ // 3. The z-order of IME might have been changed. Update the above insets state.
+ mInsetsStateController.updateAboveInsetsState(
+ mInputMethodWindow, true /* notifyInsetsChange */);
+ // 4. Update the IME control target to apply any inset change and animation.
+ // 5. Reparent the IME container surface to either the input target app, or the IME window
// parent.
updateImeControlTarget();
}
@@ -4341,14 +4330,6 @@
+ " dh=" + mDisplayInfo.logicalHeight);
}
- // Used to indicate that we have processed the insets windows. This needs to be after
- // beginLayoutLw to ensure the raw insets state display related info is initialized.
- final InsetsState rawInsetsState = getInsetsStateController().getRawInsetsState();
- mTmpAboveInsetsState = new InsetsState();
- mTmpAboveInsetsState.setDisplayFrame(rawInsetsState.getDisplayFrame());
- mTmpAboveInsetsState.setDisplayCutout(rawInsetsState.getDisplayCutout());
- mTmpAboveInsetsState.mirrorAlwaysVisibleInsetsSources(rawInsetsState);
-
int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
mLayoutSeq = seq;
@@ -5467,9 +5448,9 @@
// apply the correct override config.
changes = mAtmService.updateGlobalConfigurationLocked(values,
false /* initLocale */, false /* persistent */,
- UserHandle.USER_NULL /* userId */, deferResume);
+ UserHandle.USER_NULL /* userId */);
} else {
- changes = performDisplayOverrideConfigUpdate(values, deferResume);
+ changes = performDisplayOverrideConfigUpdate(values);
}
}
@@ -5487,7 +5468,7 @@
return kept;
}
- int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume) {
+ int performDisplayOverrideConfigUpdate(Configuration values) {
mTempConfig.setTo(getRequestedOverrideConfiguration());
final int changes = mTempConfig.updateFrom(values);
if (changes != 0) {
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index e4230a2..a37f3f2 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -21,6 +21,7 @@
import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
+import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayCutout;
@@ -70,25 +71,28 @@
* @param info the updated {@link DisplayInfo}.
* @param displayCutout the updated {@link DisplayCutout}.
* @param roundedCorners the updated {@link RoundedCorners}.
+ * @return {@code true} if the insets state has been changed; {@code false} otherwise.
*/
- public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout,
- RoundedCorners roundedCorners) {
- mDisplayWidth = info.logicalWidth;
- mDisplayHeight = info.logicalHeight;
+ public boolean onDisplayInfoUpdated(DisplayInfo info, @NonNull WmDisplayCutout displayCutout,
+ @NonNull RoundedCorners roundedCorners) {
mRotation = info.rotation;
- final WmDisplayCutout wmDisplayCutout =
- displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
final InsetsState state = mInsetsState;
- final Rect unrestricted = mUnrestricted;
final Rect safe = mDisplayCutoutSafe;
- final DisplayCutout cutout = wmDisplayCutout.getDisplayCutout();
+ final DisplayCutout cutout = displayCutout.getDisplayCutout();
+ if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight
+ && state.getDisplayCutout().equals(cutout)
+ && state.getRoundedCorners().equals(roundedCorners)) {
+ return false;
+ }
+ mDisplayWidth = info.logicalWidth;
+ mDisplayHeight = info.logicalHeight;
+ final Rect unrestricted = mUnrestricted;
unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
state.setDisplayFrame(unrestricted);
state.setDisplayCutout(cutout);
- state.setRoundedCorners(roundedCorners != null ? roundedCorners
- : RoundedCorners.NO_ROUNDED_CORNERS);
+ state.setRoundedCorners(roundedCorners);
if (!cutout.isEmpty()) {
if (cutout.getSafeInsetLeft() > 0) {
safe.left = unrestricted.left + cutout.getSafeInsetLeft();
@@ -116,6 +120,7 @@
state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT);
}
+ return true;
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index b106657..d5ded97 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -788,8 +788,13 @@
mFixedToUserRotation = fixedToUserRotation;
mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation);
- mService.updateRotation(true /* alwaysSendConfiguration */,
- false /* forceRelayout */);
+ if (mDisplayContent.mFocusedApp != null) {
+ // We record the last focused TDA that respects orientation request, check if this
+ // change may affect it.
+ mDisplayContent.onLastFocusedTaskDisplayAreaChanged(
+ mDisplayContent.mFocusedApp.getDisplayArea());
+ }
+ mDisplayContent.updateOrientation();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 580d328..75176df 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -27,6 +27,9 @@
import static android.view.InsetsState.ITYPE_INVALID;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.mandatorySystemGestures;
+import static android.view.WindowInsets.Type.systemGestures;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -293,6 +296,76 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ /**
+ * Updates {@link WindowState#mAboveInsetsState} for all windows in the display while the
+ * z-order of a window is changed.
+ *
+ * @param win The window whose z-order has changed.
+ * @param notifyInsetsChange {@code true} if the clients should be notified about the change.
+ */
+ void updateAboveInsetsState(WindowState win, boolean notifyInsetsChange) {
+ if (win == null || win.getDisplayContent() != mDisplayContent) {
+ return;
+ }
+ final boolean[] aboveWin = { true };
+ final InsetsState aboveInsetsState = new InsetsState();
+ aboveInsetsState.set(mState,
+ displayCutout() | systemGestures() | mandatorySystemGestures());
+ final SparseArray<InsetsSource> winProvidedSources = win.mProvidedInsetsSources;
+ final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
+ mDisplayContent.forAllWindows(w -> {
+ if (aboveWin[0]) {
+ if (w == win) {
+ aboveWin[0] = false;
+ if (!win.mAboveInsetsState.equals(aboveInsetsState)) {
+ win.mAboveInsetsState.set(aboveInsetsState);
+ insetsChangedWindows.add(win);
+ }
+ return winProvidedSources.size() == 0;
+ } else {
+ final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources;
+ for (int i = providedSources.size() - 1; i >= 0; i--) {
+ aboveInsetsState.addSource(providedSources.valueAt(i));
+ }
+ if (winProvidedSources.size() == 0) {
+ return false;
+ }
+ boolean changed = false;
+ for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
+ changed |= w.mAboveInsetsState.removeSource(winProvidedSources.keyAt(i));
+ }
+ if (changed) {
+ insetsChangedWindows.add(w);
+ }
+ }
+ } else {
+ for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
+ w.mAboveInsetsState.addSource(winProvidedSources.valueAt(i));
+ }
+ insetsChangedWindows.add(w);
+ }
+ return false;
+ }, true /* traverseTopToBottom */);
+ if (notifyInsetsChange) {
+ for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
+ mDispatchInsetsChanged.accept(insetsChangedWindows.get(i));
+ }
+ }
+ }
+
+ void onDisplayInfoUpdated(boolean notifyInsetsChange) {
+ final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
+ mDisplayContent.forAllWindows(w -> {
+ w.mAboveInsetsState.set(mState, displayCutout());
+ insetsChangedWindows.add(w);
+ }, true /* traverseTopToBottom */);
+ if (notifyInsetsChange) {
+ for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
+ mDispatchInsetsChanged.accept(insetsChangedWindows.get(i));
+ }
+ }
+ }
+
void onInsetsModified(InsetsControlTarget caller) {
boolean changed = false;
for (int i = mProviders.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bb5e8bf..3f9ea1f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -650,6 +650,20 @@
}
@Override
+ void dispatchConfigurationToChildren() {
+ final Configuration configuration = getConfiguration();
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ final DisplayContent displayContent = getChildAt(i);
+ if (displayContent.isDefaultDisplay) {
+ // The global configuration is also the override configuration of default display.
+ displayContent.performDisplayOverrideConfigUpdate(configuration);
+ } else {
+ displayContent.onConfigurationChanged(configuration);
+ }
+ }
+ }
+
+ @Override
public void onConfigurationChanged(Configuration newParentConfig) {
prepareFreezingTaskBounds();
super.onConfigurationChanged(newParentConfig);
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 361694b..9245f8c 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static com.android.server.wm.WindowFramesProto.COMPAT_FRAME;
import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME;
import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME;
import static com.android.server.wm.WindowFramesProto.FRAME;
@@ -177,7 +178,7 @@
mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME);
mContainingFrame.dumpDebug(proto, CONTAINING_FRAME);
mFrame.dumpDebug(proto, FRAME);
-
+ mCompatFrame.dumpDebug(proto, COMPAT_FRAME);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 39b749e..89d3040 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1881,6 +1881,10 @@
displayContent.sendNewConfiguration();
}
+ // This window doesn't have a frame yet. Don't let this window cause the insets change.
+ displayContent.getInsetsStateController().updateAboveInsetsState(
+ win, false /* notifyInsetsChanged */);
+
getInsetsSourceControls(win, outActiveControls);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a94b0aa..60e95e5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -169,7 +169,9 @@
import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME;
import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
+import static com.android.server.wm.WindowStateProto.GLOBAL_SCALE;
import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
+import static com.android.server.wm.WindowStateProto.IN_SIZE_COMPAT_MODE;
import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
@@ -211,11 +213,11 @@
import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -533,6 +535,9 @@
*/
private boolean mOrientationChanging;
+ /** The time when the window was last requested to redraw for orientation change. */
+ private long mOrientationChangeRedrawRequestTime;
+
/**
* Sometimes in addition to the mOrientationChanging
* flag we report that the orientation is changing
@@ -648,12 +653,12 @@
/**
* The insets state of sources provided by windows above the current window.
*/
- InsetsState mAboveInsetsState = new InsetsState();
+ final InsetsState mAboveInsetsState = new InsetsState();
/**
* The insets sources provided by this window.
*/
- ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>();
+ final SparseArray<InsetsSource> mProvidedInsetsSources = new SparseArray<>();
/**
* Surface insets from the previous call to relayout(), used to track
@@ -1441,11 +1446,6 @@
// redrawn; to do that, we need to go through the process of getting informed by the
// application when it has finished drawing.
if (getOrientationChanging() || dragResizingChanged) {
- if (getOrientationChanging()) {
- Slog.v(TAG_WM, "Orientation start waiting for draw"
- + ", mDrawState=DRAW_PENDING in " + this
- + ", surfaceController " + winAnimator.mSurfaceController);
- }
if (dragResizingChanged) {
ProtoLog.v(WM_DEBUG_RESIZE,
"Resize start waiting for draw, "
@@ -2204,7 +2204,7 @@
}
}
- @Override
+ @Override
void removeImmediately() {
super.removeImmediately();
@@ -3651,7 +3651,8 @@
ProtoLog.v(WM_DEBUG_RESIZE, "Reporting new frame to %s: %s", this,
mWindowFrames.mCompatFrame);
- if (mWinAnimator.mDrawState == DRAW_PENDING) {
+ final boolean drawPending = mWinAnimator.mDrawState == DRAW_PENDING;
+ if (drawPending) {
ProtoLog.i(WM_DEBUG_ORIENTATION, "Resizing %s WITH DRAW PENDING", this);
}
@@ -3668,7 +3669,7 @@
mWindowFrames.clearReportResizeHints();
final MergedConfiguration mergedConfiguration = mLastReportedConfiguration;
- final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING || useBLASTSync() || !mRedrawForSyncReported;
+ final boolean reportDraw = drawPending || useBLASTSync() || !mRedrawForSyncReported;
final boolean forceRelayout = reportOrientation || isDragResizeChanged() || !mRedrawForSyncReported;
final int displayId = getDisplayId();
fillClientWindowFrames(mClientWindowFrames);
@@ -3679,6 +3680,11 @@
mClient.resized(mClientWindowFrames, reportDraw, mergedConfiguration, forceRelayout,
getDisplayContent().getDisplayPolicy().areSystemBarsForcedShownLw(this),
displayId);
+ if (drawPending && reportOrientation && mOrientationChanging) {
+ mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime();
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Requested redraw for orientation change: %s", this);
+ }
if (mWmService.mAccessibilityController != null) {
mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId);
@@ -3998,6 +4004,8 @@
proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber);
proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
+ proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
+ proto.write(GLOBAL_SCALE, mGlobalScale);
proto.end(token);
}
@@ -5732,6 +5740,18 @@
}
boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
+ if (mOrientationChangeRedrawRequestTime > 0) {
+ final long duration =
+ SystemClock.elapsedRealtime() - mOrientationChangeRedrawRequestTime;
+ Slog.i(TAG, "finishDrawing of orientation change: " + this + " " + duration + "ms");
+ mOrientationChangeRedrawRequestTime = 0;
+ } else if (mActivityRecord != null && mActivityRecord.mRelaunchStartTime != 0
+ && mActivityRecord.findMainWindow() == this) {
+ final long duration =
+ SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime;
+ Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
+ mActivityRecord.mRelaunchStartTime = 0;
+ }
if (!onSyncFinishedDrawing()) {
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index c1c79ee..29bce79 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -97,6 +97,7 @@
"libbase",
"libappfuse",
"libbinder",
+ "libbinder_ndk",
"libcutils",
"libcrypto",
"liblog",
@@ -172,6 +173,7 @@
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
"android.frameworks.stats@1.0",
+ "android.frameworks.stats-V1-ndk_platform",
"android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend@1.0",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 729fa71..a73f6c6 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -23,6 +23,7 @@
#include <jni.h>
#include <nativehelper/JNIHelp.h>
+#include <android/binder_manager.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <binder/IServiceManager.h>
#include <hidl/HidlTransportSupport.h>
@@ -31,6 +32,7 @@
#include <schedulerservice/SchedulingPolicyService.h>
#include <sensorservice/SensorService.h>
#include <sensorservicehidl/SensorManager.h>
+#include <stats/StatsAidl.h>
#include <stats/StatsHal.h>
#include <bionic/malloc.h>
@@ -45,6 +47,31 @@
using android::base::GetIntProperty;
using namespace std::chrono_literals;
+namespace {
+
+static void startStatsAidlService() {
+ using aidl::android::frameworks::stats::IStats;
+ using aidl::android::frameworks::stats::StatsHal;
+
+ std::shared_ptr<StatsHal> statsService = ndk::SharedRefBase::make<StatsHal>();
+
+ const std::string instance = std::string() + IStats::descriptor + "/default";
+ const binder_exception_t err =
+ AServiceManager_addService(statsService->asBinder().get(), instance.c_str());
+ LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register %s: %d", instance.c_str(), err);
+}
+
+static void startStatsHidlService() {
+ using android::frameworks::stats::V1_0::IStats;
+ using android::frameworks::stats::V1_0::implementation::StatsHal;
+
+ android::sp<IStats> statsHal = new StatsHal();
+ const android::status_t err = statsHal->registerAsService();
+ LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register %s: %d", IStats::descriptor, err);
+}
+
+} // namespace
+
namespace android {
static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
@@ -54,7 +81,6 @@
SensorService::publish(false /* allowIsolated */,
IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
}
-
}
static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /* clazz */) {
@@ -62,8 +88,6 @@
using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService;
using ::android::frameworks::sensorservice::V1_0::ISensorManager;
using ::android::frameworks::sensorservice::V1_0::implementation::SensorManager;
- using ::android::frameworks::stats::V1_0::IStats;
- using ::android::frameworks::stats::V1_0::implementation::StatsHal;
using ::android::hardware::configureRpcThreadpool;
using ::android::hidl::manager::V1_0::IServiceManager;
@@ -89,9 +113,8 @@
ALOGW("%s is deprecated. Skipping registration.", ISchedulingPolicyService::descriptor);
}
- sp<IStats> statsHal = new StatsHal();
- err = statsHal->registerAsService();
- LOG_ALWAYS_FATAL_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err);
+ startStatsAidlService();
+ startStatsHidlService();
}
static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 4551d49..f054e7c 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -86,7 +86,7 @@
int pidfd = syscall(__NR_pidfd_open, pid, 0);
err = -errno;
- if (err < 0) {
+ if (pidfd < 0) {
// Skip compaction if failed to open pidfd with any error
return err;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 643503d..21d57d8 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -158,6 +158,20 @@
static struct {
jclass clazz;
jmethodID constructor;
+ jfieldID lightTypeSingle;
+ jfieldID lightTypePlayerId;
+ jfieldID lightTypeRgb;
+} gLightClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID constructor;
+ jmethodID add;
+} gArrayListClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID constructor;
jmethodID keyAt;
jmethodID valueAt;
jmethodID size;
@@ -1923,6 +1937,79 @@
return vibIdArray;
}
+static jobject nativeGetLights(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ jobject jLights = env->NewObject(gArrayListClassInfo.clazz, gArrayListClassInfo.constructor);
+
+ std::vector<int> lightIds = im->getInputManager()->getReader()->getLightIds(deviceId);
+
+ for (size_t i = 0; i < lightIds.size(); i++) {
+ const InputDeviceLightInfo* lightInfo =
+ im->getInputManager()->getReader()->getLightInfo(deviceId, lightIds[i]);
+ if (lightInfo == nullptr) {
+ ALOGW("Failed to get input device %d light info for id %d", deviceId, lightIds[i]);
+ continue;
+ }
+
+ jint jTypeId = 0;
+ if (lightInfo->type == InputDeviceLightType::SINGLE) {
+ jTypeId =
+ env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeSingle);
+ } else if (lightInfo->type == InputDeviceLightType::PLAYER_ID) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightTypePlayerId);
+ } else if (lightInfo->type == InputDeviceLightType::RGB ||
+ lightInfo->type == InputDeviceLightType::MULTI_COLOR) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeRgb);
+ } else {
+ ALOGW("Unknown light type %d", lightInfo->type);
+ continue;
+ }
+ ScopedLocalRef<jobject>
+ lightObj(env,
+ env->NewObject(gLightClassInfo.clazz, gLightClassInfo.constructor,
+ (jint)lightInfo->id, (jint)lightInfo->ordinal, jTypeId,
+ env->NewStringUTF(lightInfo->name.c_str())));
+ // Add light object to list
+ env->CallBooleanMethod(jLights, gArrayListClassInfo.add, lightObj.get());
+ }
+
+ return jLights;
+}
+
+static jint nativeGetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint lightId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ std::optional<int32_t> ret =
+ im->getInputManager()->getReader()->getLightPlayerId(deviceId, lightId);
+
+ return static_cast<jint>(ret.value_or(0));
+}
+
+static jint nativeGetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint lightId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ std::optional<int32_t> ret =
+ im->getInputManager()->getReader()->getLightColor(deviceId, lightId);
+ return static_cast<jint>(ret.value_or(0));
+}
+
+static void nativeSetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint lightId, jint playerId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->getInputManager()->getReader()->setLightPlayerId(deviceId, lightId, playerId);
+}
+
+static void nativeSetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint lightId, jint color) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->getInputManager()->getReader()->setLightColor(deviceId, lightId, color);
+}
+
static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -2192,6 +2279,11 @@
{"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate},
{"nativeIsVibrating", "(JI)Z", (void*)nativeIsVibrating},
{"nativeGetVibratorIds", "(JI)[I", (void*)nativeGetVibratorIds},
+ {"nativeGetLights", "(JI)Ljava/util/List;", (void*)nativeGetLights},
+ {"nativeGetLightPlayerId", "(JII)I", (void*)nativeGetLightPlayerId},
+ {"nativeGetLightColor", "(JII)I", (void*)nativeGetLightColor},
+ {"nativeSetLightPlayerId", "(JIII)V", (void*)nativeSetLightPlayerId},
+ {"nativeSetLightColor", "(JIII)V", (void*)nativeSetLightColor},
{"nativeGetBatteryCapacity", "(JI)I", (void*)nativeGetBatteryCapacity},
{"nativeGetBatteryStatus", "(JI)I", (void*)nativeGetBatteryStatus},
{"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts},
@@ -2386,6 +2478,27 @@
GET_METHOD_ID(gTouchCalibrationClassInfo.getAffineTransform, gTouchCalibrationClassInfo.clazz,
"getAffineTransform", "()[F");
+ // Light
+ FIND_CLASS(gLightClassInfo.clazz, "android/hardware/lights/Light");
+ gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz));
+ GET_METHOD_ID(gLightClassInfo.constructor, gLightClassInfo.clazz, "<init>",
+ "(IIILjava/lang/String;)V");
+
+ gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz));
+ gLightClassInfo.lightTypeSingle =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_SINGLE", "I");
+ gLightClassInfo.lightTypePlayerId =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_PLAYER_ID", "I");
+ gLightClassInfo.lightTypeRgb =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_RGB", "I");
+
+ // ArrayList
+ FIND_CLASS(gArrayListClassInfo.clazz, "java/util/ArrayList");
+ gArrayListClassInfo.clazz = jclass(env->NewGlobalRef(gArrayListClassInfo.clazz));
+ GET_METHOD_ID(gArrayListClassInfo.constructor, gArrayListClassInfo.clazz, "<init>", "()V");
+ GET_METHOD_ID(gArrayListClassInfo.add, gArrayListClassInfo.clazz, "add",
+ "(Ljava/lang/Object;)Z");
+
// SparseArray
FIND_CLASS(gSparseArrayClassInfo.clazz, "android/util/SparseArray");
gSparseArrayClassInfo.clazz = jclass(env->NewGlobalRef(gSparseArrayClassInfo.clazz));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7260732..6857a68 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -333,6 +333,7 @@
import org.xmlpull.v1.XmlPullParserException;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -340,6 +341,9 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -5622,7 +5626,7 @@
// Get attestation flags, if any.
final int[] attestationUtilsFlags = translateIdAttestationFlags(idAttestationFlags);
final boolean deviceIdAttestationRequired = attestationUtilsFlags != null;
- final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
+ KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
final String alias = keySpec.getKeystoreAlias();
Preconditions.checkStringNotEmpty(alias, "Empty alias provided");
@@ -5643,6 +5647,9 @@
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
}
+ if (TextUtils.isEmpty(alias)) {
+ throw new IllegalArgumentException("Empty alias provided.");
+ }
// 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) {
@@ -5651,19 +5658,26 @@
return false;
}
+ if (deviceIdAttestationRequired) {
+ if (keySpec.getAttestationChallenge() == null) {
+ throw new IllegalArgumentException(
+ "Requested Device ID attestation but challenge is empty.");
+ }
+ KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(keySpec);
+ specBuilder.setAttestationIds(attestationUtilsFlags);
+ specBuilder.setDevicePropertiesAttestationIncluded(true);
+ keySpec = specBuilder.build();
+ }
+
+ final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
final long id = mInjector.binderClearCallingIdentity();
try {
try (KeyChainConnection keyChainConnection =
- KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
+ KeyChain.bindAsUser(mContext, userHandle)) {
IKeyChainService keyChain = keyChainConnection.getService();
- // Copy the provided keySpec, excluding the attestation challenge, which will be
- // used later for requesting key attestation record.
- final KeyGenParameterSpec noAttestationSpec = new KeyGenParameterSpec.Builder(
- keySpec).setAttestationChallenge(null).build();
-
final int generationResult = keyChain.generateKeyPair(algorithm,
- new ParcelableKeyGenParameterSpec(noAttestationSpec));
+ new ParcelableKeyGenParameterSpec(keySpec));
if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
Log.e(LOG_TAG, String.format(
"KeyChain failed to generate a keypair, error %d.", generationResult));
@@ -5672,6 +5686,9 @@
throw new ServiceSpecificException(
DevicePolicyManager.KEY_GEN_STRONGBOX_UNAVAILABLE,
String.format("KeyChain error: %d", generationResult));
+ case KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS:
+ throw new UnsupportedOperationException(
+ "Device does not support Device ID attestation.");
default:
logGenerateKeyPairFailure(caller, isCredentialManagementApp);
return false;
@@ -5685,23 +5702,27 @@
// that UID.
keyChain.setGrant(caller.getUid(), alias, true);
- final byte[] attestationChallenge = keySpec.getAttestationChallenge();
- if (attestationChallenge != null) {
- final int attestationResult = keyChain.attestKey(
- alias, attestationChallenge, attestationUtilsFlags, attestationChain);
- if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) {
- Log.e(LOG_TAG, String.format(
- "Attestation for %s failed (rc=%d), deleting key.",
- alias, attestationResult));
- keyChain.removeKeyPair(alias);
- if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
- throw new UnsupportedOperationException(
- "Device does not support Device ID attestation.");
+ try {
+ final List<byte[]> encodedCerts = new ArrayList();
+ final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ final byte[] certChainBytes = keyChain.getCaCertificates(alias);
+ encodedCerts.add(keyChain.getCertificate(alias));
+ if (certChainBytes != null) {
+ final Collection<X509Certificate> certs =
+ (Collection<X509Certificate>) certFactory.generateCertificates(
+ new ByteArrayInputStream(certChainBytes));
+ for (X509Certificate cert : certs) {
+ encodedCerts.add(cert.getEncoded());
}
- logGenerateKeyPairFailure(caller, isCredentialManagementApp);
- return false;
}
+
+ attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts));
+ } catch (CertificateException e) {
+ logGenerateKeyPairFailure(caller, isCredentialManagementApp);
+ Log.e(LOG_TAG, "While retrieving certificate chain.", e);
+ return false;
}
+
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.GENERATE_KEY_PAIR)
.setAdmin(caller.getPackageName())
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 7e1e99f..5ffbd77 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -133,5 +133,8 @@
],
static_libs: [
"libgmock",
- ]
+ ],
+ test_options: {
+ unit_test: true,
+ },
}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 2fa927b..ce6e6ab 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1617,7 +1617,7 @@
// Need a shared pointer: will be passing it into all unpacking jobs.
std::shared_ptr<ZipArchive> zipFile(zipFileHandle, [](ZipArchiveHandle h) { CloseArchive(h); });
void* cookie = nullptr;
- const auto libFilePrefix = path::join(constants().libDir, abi) + "/";
+ const auto libFilePrefix = path::join(constants().libDir, abi) += "/";
if (StartIteration(zipFile.get(), &cookie, libFilePrefix, constants().libSuffix)) {
LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
return false;
@@ -1627,6 +1627,17 @@
auto openZipTs = Clock::now();
+ auto mapFiles = (mIncFs->features() & incfs::Features::v2);
+ incfs::FileId sourceId;
+ if (mapFiles) {
+ sourceId = mIncFs->getFileId(ifs->control, apkFullPath);
+ if (!incfs::isValidFileId(sourceId)) {
+ LOG(WARNING) << "Error getting IncFS file ID for apk path '" << apkFullPath
+ << "', mapping disabled";
+ mapFiles = false;
+ }
+ }
+
std::vector<Job> jobQueue;
ZipEntry entry;
std::string_view fileName;
@@ -1635,13 +1646,16 @@
continue;
}
+ const auto entryUncompressed = entry.method == kCompressStored;
+ const auto entryPageAligned = (entry.offset & (constants().blockSize - 1)) == 0;
+
if (!extractNativeLibs) {
// ensure the file is properly aligned and unpacked
- if (entry.method != kCompressStored) {
+ if (!entryUncompressed) {
LOG(WARNING) << "Library " << fileName << " must be uncompressed to mmap it";
return false;
}
- if ((entry.offset & (constants().blockSize - 1)) != 0) {
+ if (!entryPageAligned) {
LOG(WARNING) << "Library " << fileName
<< " must be page-aligned to mmap it, offset = 0x" << std::hex
<< entry.offset;
@@ -1665,6 +1679,28 @@
continue;
}
+ if (mapFiles && entryUncompressed && entryPageAligned && entry.uncompressed_length > 0) {
+ incfs::NewMappedFileParams mappedFileParams = {
+ .sourceId = sourceId,
+ .sourceOffset = entry.offset,
+ .size = entry.uncompressed_length,
+ };
+
+ if (auto res = mIncFs->makeMappedFile(ifs->control, targetLibPathAbsolute, 0755,
+ mappedFileParams);
+ res == 0) {
+ if (perfLoggingEnabled()) {
+ auto doneTs = Clock::now();
+ LOG(INFO) << "incfs: Mapped " << libName << ": "
+ << elapsedMcs(startFileTs, doneTs) << "mcs";
+ }
+ continue;
+ } else {
+ LOG(WARNING) << "Failed to map file for: '" << targetLibPath << "' errno: " << res
+ << "; falling back to full extraction";
+ }
+ }
+
// Create new lib file without signature info
incfs::NewFileParams libFileParams = {
.size = entry.uncompressed_length,
@@ -1673,7 +1709,7 @@
.metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()},
};
incfs::FileId libFileId = idFromMetadata(targetLibPath);
- if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0777, libFileId,
+ if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0755, libFileId,
libFileParams)) {
LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res;
// If one lib file fails to be created, abort others as well
@@ -1900,25 +1936,33 @@
}
IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPath(
- const IncFsMount& ifs, std::string_view storagePath, bool stopOnFirstIncomplete) const {
- ssize_t totalBlocks = 0, filledBlocks = 0;
- const auto filePaths = mFs->listFilesRecursive(storagePath);
- for (const auto& filePath : filePaths) {
+ const IncFsMount& ifs, std::string_view storagePath,
+ const bool stopOnFirstIncomplete) const {
+ ssize_t totalBlocks = 0, filledBlocks = 0, error = 0;
+ mFs->listFilesRecursive(storagePath, [&, this](auto filePath) {
const auto [filledBlocksCount, totalBlocksCount] =
mIncFs->countFilledBlocks(ifs.control, filePath);
+ if (filledBlocksCount == -EOPNOTSUPP || filledBlocksCount == -ENOTSUP ||
+ filledBlocksCount == -ENOENT) {
+ // a kind of a file that's not really being loaded, e.g. a mapped range
+ // an older IncFS used to return ENOENT in this case, so handle it the same way
+ return true;
+ }
if (filledBlocksCount < 0) {
LOG(ERROR) << "getLoadingProgress failed to get filled blocks count for: " << filePath
<< " errno: " << filledBlocksCount;
- return {filledBlocksCount, filledBlocksCount};
+ error = filledBlocksCount;
+ return false;
}
totalBlocks += totalBlocksCount;
filledBlocks += filledBlocksCount;
if (stopOnFirstIncomplete && filledBlocks < totalBlocks) {
- break;
+ return false;
}
- }
+ return true;
+ });
- return {filledBlocks, totalBlocks};
+ return error ? LoadingProgress{error, error} : LoadingProgress{filledBlocks, totalBlocks};
}
bool IncrementalService::updateLoadingProgress(
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 659d650..d613289 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -134,10 +134,11 @@
} mLooper;
};
-class RealIncFs : public IncFsWrapper {
+class RealIncFs final : public IncFsWrapper {
public:
RealIncFs() = default;
~RealIncFs() final = default;
+ Features features() const final { return incfs::features(); }
void listExistingMounts(const ExistingMountCallback& cb) const final {
for (auto mount : incfs::defaultMountRegistry().copyMounts()) {
auto binds = mount.binds(); // span() doesn't like rvalue containers, needs to save it.
@@ -153,6 +154,10 @@
incfs::NewFileParams params) const final {
return incfs::makeFile(control, path, mode, id, params);
}
+ ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode,
+ incfs::NewMappedFileParams params) const final {
+ return incfs::makeMappedFile(control, path, mode, params);
+ }
ErrorCode makeDir(const Control& control, std::string_view path, int mode) const final {
return incfs::makeDir(control, path, mode);
}
@@ -282,11 +287,10 @@
auto it = mJobs.begin();
// Always acquire begin(). We can't use it after unlock as mTimedJobs can change.
for (; it != mJobs.end() && it->when <= now; it = mJobs.begin()) {
- auto job = std::move(it->what);
- mJobs.erase(it);
+ auto jobNode = mJobs.extract(it);
lock.unlock();
- job();
+ jobNode.value().what();
lock.lock();
}
nextJobTs = it != mJobs.end() ? it->when : kInfinityTs;
@@ -308,20 +312,20 @@
std::thread mThread;
};
-class RealFsWrapper : public FsWrapper {
+class RealFsWrapper final : public FsWrapper {
public:
RealFsWrapper() = default;
~RealFsWrapper() = default;
- std::vector<std::string> listFilesRecursive(std::string_view directoryPath) const final {
- std::vector<std::string> files;
+ void listFilesRecursive(std::string_view directoryPath, FileCallback onFile) const final {
for (const auto& entry : std::filesystem::recursive_directory_iterator(directoryPath)) {
if (!entry.is_regular_file()) {
continue;
}
- files.push_back(entry.path().c_str());
+ if (!onFile(entry.path().native())) {
+ break;
+ }
}
- return files;
}
};
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index d60035a..245bb31 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android-base/function_ref.h>
#include <android-base/unique_fd.h>
#include <android/content/pm/DataLoaderParamsParcel.h>
#include <android/content/pm/FileSystemControlParcel.h>
@@ -77,18 +78,22 @@
using ErrorCode = incfs::ErrorCode;
using UniqueFd = incfs::UniqueFd;
using WaitResult = incfs::WaitResult;
+ using Features = incfs::Features;
- using ExistingMountCallback =
- std::function<void(std::string_view root, std::string_view backingDir,
- std::span<std::pair<std::string_view, std::string_view>> binds)>;
+ using ExistingMountCallback = android::base::function_ref<
+ void(std::string_view root, std::string_view backingDir,
+ std::span<std::pair<std::string_view, std::string_view>> binds)>;
virtual ~IncFsWrapper() = default;
+ virtual Features features() const = 0;
virtual void listExistingMounts(const ExistingMountCallback& cb) const = 0;
virtual Control openMount(std::string_view path) const = 0;
virtual Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs,
IncFsFd blocksWritten) const = 0;
virtual ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id,
incfs::NewFileParams params) const = 0;
+ virtual ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode,
+ incfs::NewMappedFileParams params) const = 0;
virtual ErrorCode makeDir(const Control& control, std::string_view path, int mode) const = 0;
virtual ErrorCode makeDirs(const Control& control, std::string_view path, int mode) const = 0;
virtual incfs::RawMetadata getMetadata(const Control& control, FileId fileid) const = 0;
@@ -148,7 +153,9 @@
class FsWrapper {
public:
virtual ~FsWrapper() = default;
- virtual std::vector<std::string> listFilesRecursive(std::string_view directoryPath) const = 0;
+
+ using FileCallback = android::base::function_ref<bool(std::string_view)>;
+ virtual void listFilesRecursive(std::string_view directoryPath, FileCallback onFile) const = 0;
};
class ServiceManagerWrapper {
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
new file mode 100644
index 0000000..d935256
--- /dev/null
+++ b/services/incremental/TEST_MAPPING
@@ -0,0 +1,32 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
+ },
+ {
+ "include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
+ },
+ {
+ "include-filter": "android.content.pm.cts.ChecksumsTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageManagerStatsHostTestCases",
+ "options": [
+ {
+ "include-filter": "com.android.cts.packagemanager.stats.host.PackageInstallerV2StatsTests"
+ }
+ ]
+ },
+ {
+ "name": "CtsIncrementalInstallHostTestCases"
+ },
+ {
+ "name": "CtsInstalledLoadingProgressHostTests"
+ }
+ ]
+}
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index ab491ef..b00a84f 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -322,6 +322,7 @@
class MockIncFs : public IncFsWrapper {
public:
+ MOCK_CONST_METHOD0(features, Features());
MOCK_CONST_METHOD1(listExistingMounts, void(const ExistingMountCallback& cb));
MOCK_CONST_METHOD1(openMount, Control(std::string_view path));
MOCK_CONST_METHOD4(createControl,
@@ -330,6 +331,9 @@
MOCK_CONST_METHOD5(makeFile,
ErrorCode(const Control& control, std::string_view path, int mode, FileId id,
NewFileParams params));
+ MOCK_CONST_METHOD4(makeMappedFile,
+ ErrorCode(const Control& control, std::string_view path, int mode,
+ NewMappedFileParams params));
MOCK_CONST_METHOD3(makeDir, ErrorCode(const Control& control, std::string_view path, int mode));
MOCK_CONST_METHOD3(makeDirs,
ErrorCode(const Control& control, std::string_view path, int mode));
@@ -539,16 +543,16 @@
class MockFsWrapper : public FsWrapper {
public:
- MOCK_CONST_METHOD1(listFilesRecursive, std::vector<std::string>(std::string_view));
- void hasNoFile() {
- ON_CALL(*this, listFilesRecursive(_)).WillByDefault(Return(std::vector<std::string>()));
- }
+ MOCK_CONST_METHOD2(listFilesRecursive, void(std::string_view, FileCallback));
+ void hasNoFile() { ON_CALL(*this, listFilesRecursive(_, _)).WillByDefault(Return()); }
void hasFiles() {
- ON_CALL(*this, listFilesRecursive(_))
+ ON_CALL(*this, listFilesRecursive(_, _))
.WillByDefault(Invoke(this, &MockFsWrapper::fakeFiles));
}
- std::vector<std::string> fakeFiles(std::string_view directoryPath) {
- return {"base.apk", "split.apk", "lib/a.so"};
+ void fakeFiles(std::string_view directoryPath, FileCallback onFile) {
+ for (auto file : {"base.apk", "split.apk", "lib/a.so"}) {
+ if (!onFile(file)) break;
+ }
}
};
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dd2dd81..6bbd320 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -104,7 +104,6 @@
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
-import com.android.server.apphibernation.AppHibernationService;
import com.android.server.attention.AttentionManagerService;
import com.android.server.audio.AudioService;
import com.android.server.biometrics.AuthService;
@@ -1718,14 +1717,9 @@
startTextToSpeechManagerService(context, t);
// System Speech Recognition Service
- if (deviceHasConfigString(context,
- R.string.config_defaultOnDeviceSpeechRecognitionService)) {
- t.traceBegin("StartSpeechRecognitionManagerService");
- mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS);
- t.traceEnd();
- } else {
- Slog.d(TAG, "System speech recognition is not defined by OEM");
- }
+ t.traceBegin("StartSpeechRecognitionManagerService");
+ mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
// App prediction manager service
if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) {
@@ -1785,7 +1779,7 @@
t.traceBegin("StartIpSecService");
try {
- ipSecService = IpSecService.create(context, networkManagement);
+ ipSecService = IpSecService.create(context);
ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
} catch (Throwable e) {
reportWtf("starting IpSec Service", e);
@@ -2150,11 +2144,9 @@
mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
t.traceEnd();
- if (AppHibernationService.isAppHibernationEnabled()) {
- t.traceBegin("StartAppHibernationService");
- mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS);
- t.traceEnd();
- }
+ t.traceBegin("StartAppHibernationService");
+ mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS);
+ t.traceEnd();
if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
t.traceBegin("StartGestureLauncher");
diff --git a/services/musicrecognition/Android.bp b/services/musicrecognition/Android.bp
index fea9efa..8298dec 100644
--- a/services/musicrecognition/Android.bp
+++ b/services/musicrecognition/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.musicsearch-sources",
srcs: ["java/**/*.java"],
@@ -10,4 +19,4 @@
defaults: ["platform_service_defaults"],
srcs: [":services.musicsearch-sources"],
libs: ["services.core", "app-compat-annotations"],
-}
\ No newline at end of file
+}
diff --git a/services/searchui/Android.bp b/services/searchui/Android.bp
index cc63294..3081a51 100644
--- a/services/searchui/Android.bp
+++ b/services/searchui/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.searchui-sources",
srcs: ["java/**/*.java"],
diff --git a/services/smartspace/Android.bp b/services/smartspace/Android.bp
index fcf780d..640a88d 100644
--- a/services/smartspace/Android.bp
+++ b/services/smartspace/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.smartspace-sources",
srcs: ["java/**/*.java"],
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
index b7a0624..e70a734 100644
--- a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "PackageManagerServiceHostTestsIntentVerifyUtils",
srcs: ["src/**/*.kt"],
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
index af0ac77..7e4f0e7 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "PackageManagerServiceDeviceSideTests",
sdk_version: "test_current",
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
index e82f57d..4f3f2eb 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "PackageManagerTestIntentVerifier",
srcs: [ "src/**/*.kt" ],
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
index 7161fdd..9f9ed24 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "PackageManagerTestIntentVerifierTarget1",
manifest: "AndroidManifest1.xml",
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
index 58f17f2..ebad5af 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "PackageManagerTestAppDeclaresStaticLibrary",
manifest: "AndroidManifestDeclaresStaticLibrary.xml",
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index 4aa8abc..334e53a 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PackageManagerServiceUnitTests",
srcs: ["src/**/*.kt"],
diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp
index 6dd059f..7c237ac 100644
--- a/services/tests/inprocesstests/Android.bp
+++ b/services/tests/inprocesstests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksInProcessTests",
srcs: ["src/**/*.java"],
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index 928065a..a32bf2c 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libactivitymanagermockingservicestestjni",
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 56d30cc..20a5842 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -139,7 +139,8 @@
app.info.uid = packageUid;
// Exact value does not mater, it can be any state for which compaction is allowed.
app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- app.mState.setSetAdj(905);
+ app.mState.setSetAdj(899);
+ app.mState.setCurAdj(940);
return app;
}
@@ -164,8 +165,6 @@
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
- assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
@@ -176,6 +175,11 @@
CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
CachedAppOptimizer.DEFAULT_USE_FREEZER);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
+
Set<Integer> expected = new HashSet<>();
for (String s : TextUtils.split(
@@ -231,6 +235,14 @@
Long.toString(
CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ,
+ Long.toString(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 10), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ,
+ Long.toString(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 10), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
CachedAppOptimizer.DEFAULT_USE_FREEZER);
@@ -261,6 +273,12 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 10);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 10);
assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
@@ -437,7 +455,7 @@
mCachedAppOptimizerUnderTest.init();
// When we override new reasonable throttle values after init...
- mCountDown = new CountDownLatch(6);
+ mCountDown = new CountDownLatch(8);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_COMPACT_THROTTLE_1,
Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
@@ -456,7 +474,13 @@
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_COMPACT_THROTTLE_6,
Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ,
+ Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1), false);
+ assertThat(mCountDown.await(7, TimeUnit.SECONDS)).isTrue();
// Then those flags values are reflected in the compactor.
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
@@ -471,6 +495,10 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1);
}
@Test
@@ -902,7 +930,6 @@
valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get(
pid).getRssAfterCompaction();
assertThat(valuesAfter).isEqualTo(rssAfter3);
-
}
@Test
@@ -954,6 +981,54 @@
assertThat(valuesAfter).isEqualTo(rssAboveThresholdAfter);
}
+ @Test
+ public void processWithOomAdjTooSmall_notFullCompacted() throws Exception {
+ // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set Min and
+ // Max OOM_Adj throttles.
+ mCachedAppOptimizerUnderTest.init();
+ setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true);
+ setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, Long.toString(920), true);
+ setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, Long.toString(950), true);
+ initActivityManagerService();
+
+ // Simulate RSS memory for which compaction should occur.
+ long[] rssBefore =
+ new long[]{/*Total RSS*/ 15000, /*File RSS*/ 15000, /*Anon RSS*/ 15000,
+ /*Swap*/ 10000};
+ long[] rssAfter =
+ new long[]{/*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/5000};
+ // Process that passes properties.
+ int pid = 1;
+ ProcessRecord processRecord =
+ makeProcessRecord(pid, 2, 3, "p1", "app1");
+ mProcessDependencies.setRss(rssBefore);
+ mProcessDependencies.setRssAfterCompaction(rssAfter);
+
+ // Compaction should occur if (setAdj < min for process || setAdj > max for process) &&
+ // (MIN < curAdj < MAX)
+ // GIVEN OomAdj score below threshold.
+ processRecord.mState.setSetAdj(899);
+ processRecord.mState.setCurAdj(970);
+ // WHEN we try to run compaction
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+ waitForHandler();
+ // THEN process IS NOT compacted.
+ assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
+
+ // GIVEN (setAdj < MIN || setAdj > MAX) && (MIN < curAdj < MAX)
+ processRecord.mState.setSetAdj(910);
+ processRecord.mState.setCurAdj(930);
+ // WHEN we try to run compaction
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+ waitForHandler();
+ // THEN process IS compacted.
+ assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
+ long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats
+ .get(pid)
+ .getRssAfterCompaction();
+ assertThat(valuesAfter).isEqualTo(rssAfter);
+ }
+
private void setFlag(String key, String value, boolean defaultValue) throws Exception {
mCountDown = new CountDownLatch(1);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 27825a4..a382e85 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -64,6 +64,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.answer;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.doAnswer;
@@ -73,6 +74,8 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.content.ComponentName;
@@ -178,11 +181,20 @@
setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
setFieldValue(ActivityManagerService.class, sService, "mProcLock",
new ActivityManagerProcLock());
+ setFieldValue(ActivityManagerService.class, sService, "mServices",
+ spy(new ActiveServices(sService)));
+ setFieldValue(ActivityManagerService.class, sService, "mInternal",
+ mock(ActivityManagerService.LocalService.class));
+ setFieldValue(ActivityManagerService.class, sService, "mBatteryStatsService",
+ mock(BatteryStatsService.class));
+ doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager();
+ doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
+ doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(any(String.class));
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList,
- mock(ActiveUids.class));
+ new ActiveUids(sService, false));
sService.mOomAdjuster.mAdjSeq = 10000;
sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
}
@@ -1315,6 +1327,115 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_UidIdle_StopService() {
+ final ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ final ProcessRecord client1 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+ MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+ final ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP3_UID,
+ MOCKAPP4_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+ final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
+ MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
+ final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, sService);
+ final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, sService);
+ final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, sService);
+ final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, sService);
+ app1.setUidRecord(app1UidRecord);
+ app2.setUidRecord(app2UidRecord);
+ app3.setUidRecord(app3UidRecord);
+ client1.setUidRecord(clientUidRecord);
+ client2.setUidRecord(clientUidRecord);
+
+ client1.mServices.setHasForegroundServices(true, 0);
+ client2.mState.setForcingToImportant(new Object());
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
+ lru.clear();
+ lru.add(app1);
+ lru.add(app2);
+ lru.add(app3);
+ lru.add(client1);
+ lru.add(client2);
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+
+ final ComponentName cn1 = ComponentName.unflattenFromString(
+ MOCKAPP_PACKAGENAME + "/.TestService");
+ final ServiceRecord s1 = bindService(app1, client1, null, 0, mock(IBinder.class));
+ setFieldValue(ServiceRecord.class, s1, "name", cn1);
+ s1.startRequested = true;
+
+ final ComponentName cn2 = ComponentName.unflattenFromString(
+ MOCKAPP2_PACKAGENAME + "/.TestService");
+ final ServiceRecord s2 = bindService(app2, client2, null, 0, mock(IBinder.class));
+ setFieldValue(ServiceRecord.class, s2, "name", cn2);
+ s2.startRequested = true;
+
+ final ComponentName cn3 = ComponentName.unflattenFromString(
+ MOCKAPP5_PACKAGENAME + "/.TestService");
+ final ServiceRecord s3 = bindService(app3, client1, null, 0, mock(IBinder.class));
+ setFieldValue(ServiceRecord.class, s3, "name", cn3);
+ s3.startRequested = true;
+
+ final ComponentName cn4 = ComponentName.unflattenFromString(
+ MOCKAPP3_PACKAGENAME + "/.TestService");
+ final ServiceRecord c2s = makeServiceRecord(client2);
+ setFieldValue(ServiceRecord.class, c2s, "name", cn4);
+ c2s.startRequested = true;
+
+ try {
+ sService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord);
+ sService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord);
+ sService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord);
+ sService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord);
+
+ setServiceMap(s1, MOCKAPP_UID, cn1);
+ setServiceMap(s2, MOCKAPP2_UID, cn2);
+ setServiceMap(s3, MOCKAPP5_UID, cn3);
+ setServiceMap(c2s, MOCKAPP3_UID, cn4);
+ app2UidRecord.setIdle(false);
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app1, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT);
+ assertProcStates(app3, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT);
+ assertProcStates(client1, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT);
+ assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app2.mState.getSetProcState());
+ assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, client2.mState.getSetProcState());
+
+ client1.mServices.setHasForegroundServices(false, 0);
+ client2.mState.setForcingToImportant(null);
+ app1UidRecord.reset();
+ app2UidRecord.reset();
+ app3UidRecord.reset();
+ clientUidRecord.reset();
+ app1UidRecord.setIdle(true);
+ app2UidRecord.setIdle(true);
+ app3UidRecord.setIdle(true);
+ clientUidRecord.setIdle(true);
+ doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService)
+ .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
+ anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
+ doNothing().when(sService.mServices)
+ .scheduleServiceTimeoutLocked(any(ProcessRecord.class));
+ sService.mOomAdjuster.updateOomAdjLocked(client1, OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertEquals(PROCESS_STATE_CACHED_EMPTY, client1.mState.getSetProcState());
+ assertEquals(PROCESS_STATE_SERVICE, app1.mState.getSetProcState());
+ assertEquals(PROCESS_STATE_SERVICE, client2.mState.getSetProcState());
+ } finally {
+ doCallRealMethod().when(sService)
+ .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
+ anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
+ sService.mServices.mServiceMap.clear();
+ sService.mOomAdjuster.mActiveUids.clear();
+ }
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoAll_Unbound() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
@@ -1815,15 +1936,42 @@
return app;
}
+ private ServiceRecord makeServiceRecord(ProcessRecord app) {
+ final ServiceRecord record = mock(ServiceRecord.class);
+ record.app = app;
+ setFieldValue(ServiceRecord.class, record, "connections",
+ new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
+ doCallRealMethod().when(record).getConnections();
+ setFieldValue(ServiceRecord.class, record, "packageName", app.info.packageName);
+ app.mServices.startService(record);
+ record.appInfo = app.info;
+ setFieldValue(ServiceRecord.class, record, "bindings", new ArrayMap<>());
+ setFieldValue(ServiceRecord.class, record, "pendingStarts", new ArrayList<>());
+ return record;
+ }
+
+ private void setServiceMap(ServiceRecord s, int uid, ComponentName cn) {
+ ActiveServices.ServiceMap serviceMap = sService.mServices.mServiceMap.get(
+ UserHandle.getUserId(uid));
+ if (serviceMap == null) {
+ serviceMap = mock(ActiveServices.ServiceMap.class);
+ setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mServicesByInstanceName",
+ new ArrayMap<>());
+ setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mActiveForegroundApps",
+ new ArrayMap<>());
+ setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mServicesByIntent",
+ new ArrayMap<>());
+ setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mDelayedStartList",
+ new ArrayList<>());
+ sService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap);
+ }
+ serviceMap.mServicesByInstanceName.put(cn, s);
+ }
+
private ServiceRecord bindService(ProcessRecord service, ProcessRecord client,
ServiceRecord record, int bindFlags, IBinder binder) {
if (record == null) {
- record = mock(ServiceRecord.class);
- record.app = service;
- setFieldValue(ServiceRecord.class, record, "connections",
- new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
- service.mServices.startService(record);
- doCallRealMethod().when(record).getConnections();
+ record = makeServiceRecord(service);
}
AppBindRecord binding = new AppBindRecord(record, null, client);
ConnectionRecord cr = spy(new ConnectionRecord(binding,
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index ca53492..728b97c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -25,8 +25,10 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
@@ -94,11 +96,13 @@
private Injector mInjector;
+ @Mock
+ private LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy;
+
@Before
public void setUp() throws Exception {
mMockitoSession = mockitoSession()
.initMocks(this)
- .mockStatic(SurfaceControl.class)
.strictness(Strictness.LENIENT)
.startMocking();
mHandler = new Handler(Looper.getMainLooper());
@@ -223,10 +227,11 @@
for (int i = 0; i < wrappedModes.length; i++) {
modes[i] = wrappedModes[i].mode;
}
- display.modes = modes;
+ display.dynamicInfo.supportedDisplayModes = modes;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertTrue(mListener.traversalRequested);
assertThat(mListener.changedDisplays.size()).isGreaterThan(0);
// Verify the supported modes are updated accordingly.
@@ -252,53 +257,53 @@
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
new DisplayModeWrapper(
- createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{24f, 50f}),
+ createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{24f, 50f}),
new DisplayModeWrapper(
- createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{24f, 60f}),
+ createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{24f, 60f}),
new DisplayModeWrapper(
- createFakeDisplayMode(1920, 1080, 24f, 0), new float[]{50f, 60f}),
+ createFakeDisplayMode(2, 1920, 1080, 24f, 0), new float[]{50f, 60f}),
new DisplayModeWrapper(
- createFakeDisplayMode(3840, 2160, 60f, 0), new float[]{24f, 50f}),
+ createFakeDisplayMode(3, 3840, 2160, 60f, 0), new float[]{24f, 50f}),
new DisplayModeWrapper(
- createFakeDisplayMode(3840, 2160, 50f, 0), new float[]{24f, 60f}),
+ createFakeDisplayMode(4, 3840, 2160, 50f, 0), new float[]{24f, 60f}),
new DisplayModeWrapper(
- createFakeDisplayMode(3840, 2160, 24f, 0), new float[]{50f, 60f}),
+ createFakeDisplayMode(5, 3840, 2160, 24f, 0), new float[]{50f, 60f}),
});
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
new DisplayModeWrapper(
- createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{50f}),
+ createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{50f}),
new DisplayModeWrapper(
- createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{60f}),
+ createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{60f}),
new DisplayModeWrapper(
- createFakeDisplayMode(1920, 1080, 24f, 1), new float[0]),
+ createFakeDisplayMode(2, 1920, 1080, 24f, 1), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayMode(3840, 2160, 60f, 2), new float[0]),
+ createFakeDisplayMode(3, 3840, 2160, 60f, 2), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayMode(3840, 2160, 50f, 3), new float[]{24f}),
+ createFakeDisplayMode(4, 3840, 2160, 50f, 3), new float[]{24f}),
new DisplayModeWrapper(
- createFakeDisplayMode(3840, 2160, 24f, 3), new float[]{50f}),
+ createFakeDisplayMode(5, 3840, 2160, 24f, 3), new float[]{50f}),
});
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
new DisplayModeWrapper(
- createFakeDisplayMode(1920, 1080, 60f, 0), new float[0]),
+ createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayMode(1920, 1080, 50f, 1), new float[0]),
+ createFakeDisplayMode(1, 1920, 1080, 50f, 1), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayMode(1920, 1080, 24f, 2), new float[0]),
+ createFakeDisplayMode(2, 1920, 1080, 24f, 2), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayMode(3840, 2160, 60f, 3), new float[0]),
+ createFakeDisplayMode(3, 3840, 2160, 60f, 3), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayMode(3840, 2160, 50f, 4), new float[0]),
+ createFakeDisplayMode(4, 3840, 2160, 50f, 4), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayMode(3840, 2160, 24f, 5), new float[0]),
+ createFakeDisplayMode(5, 3840, 2160, 24f, 5), new float[0]),
});
}
@Test
public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception {
- SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(1920, 1080, 60f);
+ SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(0, 1920, 1080, 60f);
SurfaceControl.DisplayMode[] modes =
new SurfaceControl.DisplayMode[]{displayMode};
FakeDisplay display = new FakeDisplay(PORT_A, modes, 0);
@@ -325,18 +330,15 @@
displayMode.refreshRate)).isTrue();
// Change the display
- SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3840, 2160,
- 60f);
+ SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(1, 3840, 2160, 60f);
modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo};
- display.modes = modes;
- display.activeMode = 1;
+ display.dynamicInfo.supportedDisplayModes = modes;
+ display.dynamicInfo.activeDisplayModeId = 1;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
- assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
- assertThat(SurfaceControl.getDisplayModes(display.token).length).isEqualTo(2);
-
+ assertTrue(mListener.traversalRequested);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -360,8 +362,8 @@
@Test
public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception {
SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
- createFakeDisplayMode(1920, 1080, 60f),
- createFakeDisplayMode(1920, 1080, 50f)
+ createFakeDisplayMode(0, 1920, 1080, 60f),
+ createFakeDisplayMode(1, 1920, 1080, 50f)
};
FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0);
setUpDisplay(display);
@@ -379,13 +381,12 @@
assertThat(activeMode.matches(1920, 1080, 60f)).isTrue();
// Change the display
- display.activeMode = 1;
+ display.dynamicInfo.activeDisplayModeId = 1;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
- assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
-
+ assertTrue(mListener.traversalRequested);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -402,7 +403,7 @@
FakeDisplay display = new FakeDisplay(PORT_A);
Display.HdrCapabilities initialHdrCapabilities = new Display.HdrCapabilities(new int[0],
1000, 1000, 0);
- display.hdrCapabilities = initialHdrCapabilities;
+ display.dynamicInfo.hdrCapabilities = initialHdrCapabilities;
setUpDisplay(display);
updateAvailableDisplays();
mAdapter.registerLocked();
@@ -419,11 +420,12 @@
// Change the display
Display.HdrCapabilities changedHdrCapabilities = new Display.HdrCapabilities(
new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0);
- display.hdrCapabilities = changedHdrCapabilities;
+ display.dynamicInfo.hdrCapabilities = changedHdrCapabilities;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertTrue(mListener.traversalRequested);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -435,10 +437,78 @@
}
@Test
+ public void testAfterDisplayChange_AllmSupportIsUpdated() throws Exception {
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ display.dynamicInfo.autoLowLatencyModeSupported = true;
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays).isEmpty();
+
+ DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0)
+ .getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.allmSupported).isTrue();
+
+ // Change the display
+ display.dynamicInfo.autoLowLatencyModeSupported = false;
+ setUpDisplay(display);
+ mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertTrue(mListener.traversalRequested);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+
+ DisplayDevice displayDevice = mListener.changedDisplays.get(0);
+ displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+ displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.allmSupported).isFalse();
+ }
+
+ @Test
+ public void testAfterDisplayChange_GameContentTypeSupportIsUpdated() throws Exception {
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ display.dynamicInfo.gameContentTypeSupported = true;
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays).isEmpty();
+
+ DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0)
+ .getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.gameContentTypeSupported).isTrue();
+
+ // Change the display
+ display.dynamicInfo.gameContentTypeSupported = false;
+ setUpDisplay(display);
+ mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertTrue(mListener.traversalRequested);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+
+ DisplayDevice displayDevice = mListener.changedDisplays.get(0);
+ displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+ displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.gameContentTypeSupported).isFalse();
+ }
+
+ @Test
public void testAfterDisplayChange_ColorModesAreUpdated() throws Exception {
FakeDisplay display = new FakeDisplay(PORT_A);
final int[] initialColorModes = new int[]{Display.COLOR_MODE_BT709};
- display.colorModes = initialColorModes;
+ display.dynamicInfo.supportedColorModes = initialColorModes;
setUpDisplay(display);
updateAvailableDisplays();
mAdapter.registerLocked();
@@ -455,11 +525,12 @@
// Change the display
final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT};
- display.colorModes = changedColorModes;
+ display.dynamicInfo.supportedColorModes = changedColorModes;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertTrue(mListener.traversalRequested);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -474,8 +545,8 @@
@Test
public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception {
SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
- createFakeDisplayMode(1920, 1080, 60f),
- createFakeDisplayMode(1920, 1080, 50f)
+ createFakeDisplayMode(0, 1920, 1080, 60f),
+ createFakeDisplayMode(1, 1920, 1080, 50f)
};
final int activeMode = 0;
FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode);
@@ -486,45 +557,97 @@
mAdapter.registerLocked();
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+ int baseModeId = Arrays.stream(displayDevice.getDisplayDeviceInfoLocked().supportedModes)
+ .filter(mode -> mode.getRefreshRate() == 60f)
+ .findFirst()
+ .get()
+ .getModeId();
+
+ displayDevice.setDesiredDisplayModeSpecsLocked(
+ new DisplayModeDirector.DesiredDisplayModeSpecs(
+ /*baseModeId*/ baseModeId,
+ /*allowGroupSwitching*/ false,
+ new DisplayModeDirector.RefreshRateRange(60f, 60f),
+ new DisplayModeDirector.RefreshRateRange(60f, 60f)
+ ));
+ verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token,
+ new SurfaceControl.DesiredDisplayModeSpecs(
+ /* baseModeId */ 0,
+ /* allowGroupSwitching */ false,
+ /* primaryRange */ 60f, 60f,
+ /* appRange */ 60f, 60f
+ ));
+
// Change the display
- display.modes = new SurfaceControl.DisplayMode[]{
- createFakeDisplayMode(1920, 1080, 60f)
+ display.dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(2, 1920, 1080, 60f)
};
- // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't
- // trigger ArrayOutOfBoundsException.
+ display.dynamicInfo.activeDisplayModeId = 2;
+ // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't crash.
display.desiredDisplayModeSpecs.defaultMode = 1;
setUpDisplay(display);
- updateAvailableDisplays();
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertTrue(mListener.traversalRequested);
+
+ displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+
+ baseModeId = displayDevice.getDisplayDeviceInfoLocked().supportedModes[0].getModeId();
+
+ // The traversal request will call setDesiredDisplayModeSpecsLocked on the display device
+ displayDevice.setDesiredDisplayModeSpecsLocked(
+ new DisplayModeDirector.DesiredDisplayModeSpecs(
+ /*baseModeId*/ baseModeId,
+ /*allowGroupSwitching*/ false,
+ new DisplayModeDirector.RefreshRateRange(60f, 60f),
+ new DisplayModeDirector.RefreshRateRange(60f, 60f)
+ ));
+
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // Verify that this will reapply the desired modes.
+ verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token,
+ new SurfaceControl.DesiredDisplayModeSpecs(
+ /* baseModeId */ 2,
+ /* allowGroupSwitching */ false,
+ /* primaryRange */ 60f, 60f,
+ /* appRange */ 60f, 60f
+ ));
}
@Test
public void testBacklightAdapter_withSurfaceControlSupport() {
final Binder displayToken = new Binder();
- doReturn(true).when(() -> SurfaceControl.getDisplayBrightnessSupport(displayToken));
+
+ when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(true);
// Test as default display
- BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/);
+ BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/,
+ mSurfaceControlProxy);
ba.setBrightness(0.514f);
- verify(() -> SurfaceControl.setDisplayBrightness(displayToken, 0.514f));
+ verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.514f);
// Test as not default display
- BacklightAdapter ba2 = new BacklightAdapter(displayToken,
- false /*isDefault*/);
+ BacklightAdapter ba2 = new BacklightAdapter(displayToken, false /*isDefault*/,
+ mSurfaceControlProxy);
ba2.setBrightness(0.323f);
- verify(() -> SurfaceControl.setDisplayBrightness(displayToken, 0.323f));
+ verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.323f);
}
@Test
public void testBacklightAdapter_withoutSourceControlSupport_defaultDisplay() {
final Binder displayToken = new Binder();
- doReturn(false).when(() -> SurfaceControl.getDisplayBrightnessSupport(displayToken));
+ when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false);
doReturn(mMockedBacklight).when(mMockedLightsManager)
.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
- BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/);
+ BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/,
+ mSurfaceControlProxy);
ba.setBrightness(0.123f);
verify(mMockedBacklight).setBrightness(0.123f);
}
@@ -532,11 +655,12 @@
@Test
public void testBacklightAdapter_withoutSourceControlSupport_nonDefaultDisplay() {
final Binder displayToken = new Binder();
- doReturn(false).when(() -> SurfaceControl.getDisplayBrightnessSupport(displayToken));
+ when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false);
doReturn(mMockedBacklight).when(mMockedLightsManager)
.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
- BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/);
+ BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/,
+ mSurfaceControlProxy);
ba.setBrightness(0.456f);
// Adapter does not forward any brightness in this case.
@@ -588,12 +712,15 @@
private static class FakeDisplay {
public final DisplayAddress.Physical address;
public final IBinder token = new Binder();
- public final SurfaceControl.DisplayInfo info;
- public SurfaceControl.DisplayMode[] modes;
- public int activeMode;
- public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
- public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
- 1000, 1000, 0);
+ public final SurfaceControl.StaticDisplayInfo info;
+ public SurfaceControl.DynamicDisplayInfo dynamicInfo =
+ new SurfaceControl.DynamicDisplayInfo();
+ {
+ dynamicInfo.supportedColorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
+ dynamicInfo.hdrCapabilities = new Display.HdrCapabilities(new int[0],
+ 1000, 1000, 0);
+ }
+
public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0,
/* allowGroupSwitching */ false,
@@ -603,37 +730,32 @@
/* appRefreshRateMax */60.f);
private FakeDisplay(int port) {
- this.address = createDisplayAddress(port);
- this.info = createFakeDisplayInfo();
- this.modes = new SurfaceControl.DisplayMode[]{
- createFakeDisplayMode(800, 600, 60f)
+ address = createDisplayAddress(port);
+ info = createFakeDisplayInfo();
+ dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(0, 800, 600, 60f)
};
- this.activeMode = 0;
+ dynamicInfo.activeDisplayModeId = 0;
}
private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode) {
- this.address = createDisplayAddress(port);
- this.info = createFakeDisplayInfo();
- this.modes = modes;
- this.activeMode = activeMode;
+ address = createDisplayAddress(port);
+ info = createFakeDisplayInfo();
+ dynamicInfo.supportedDisplayModes = modes;
+ dynamicInfo.activeDisplayModeId = activeMode;
}
}
private void setUpDisplay(FakeDisplay display) {
mAddresses.add(display.address);
- doReturn(display.token).when(() ->
- SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId()));
- doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token));
- doReturn(display.modes).when(
- () -> SurfaceControl.getDisplayModes(display.token));
- doReturn(display.activeMode).when(() -> SurfaceControl.getActiveDisplayMode(display.token));
- doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
- doReturn(display.colorModes).when(
- () -> SurfaceControl.getDisplayColorModes(display.token));
- doReturn(display.hdrCapabilities).when(
- () -> SurfaceControl.getHdrCapabilities(display.token));
- doReturn(display.desiredDisplayModeSpecs)
- .when(() -> SurfaceControl.getDesiredDisplayModeSpecs(display.token));
+ when(mSurfaceControlProxy.getPhysicalDisplayToken(display.address.getPhysicalDisplayId()))
+ .thenReturn(display.token);
+ when(mSurfaceControlProxy.getStaticDisplayInfo(display.token))
+ .thenReturn(display.info);
+ when(mSurfaceControlProxy.getDynamicDisplayInfo(display.token))
+ .thenReturn(display.dynamicInfo);
+ when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token))
+ .thenReturn(display.desiredDisplayModeSpecs);
}
private void updateAvailableDisplays() {
@@ -643,27 +765,28 @@
ids[i] = address.getPhysicalDisplayId();
i++;
}
- doReturn(ids).when(() -> SurfaceControl.getPhysicalDisplayIds());
+ when(mSurfaceControlProxy.getPhysicalDisplayIds()).thenReturn(ids);
}
private static DisplayAddress.Physical createDisplayAddress(int port) {
return DisplayAddress.fromPortAndModel(port, DISPLAY_MODEL);
}
- private static SurfaceControl.DisplayInfo createFakeDisplayInfo() {
- final SurfaceControl.DisplayInfo info = new SurfaceControl.DisplayInfo();
+ private static SurfaceControl.StaticDisplayInfo createFakeDisplayInfo() {
+ final SurfaceControl.StaticDisplayInfo info = new SurfaceControl.StaticDisplayInfo();
info.density = 100;
return info;
}
- private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
+ private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height,
float refreshRate) {
- return createFakeDisplayMode(width, height, refreshRate, 0);
+ return createFakeDisplayMode(id, width, height, refreshRate, /* group */ 0);
}
- private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
+ private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height,
float refreshRate, int group) {
final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode();
+ mode.id = id;
mode.width = width;
mode.height = height;
mode.refreshRate = refreshRate;
@@ -710,14 +833,21 @@
LocalDisplayAdapter.DisplayEventListener listener) {
mTransmitter = new HotplugTransmitter(looper, listener);
}
+
public HotplugTransmitter getTransmitter() {
return mTransmitter;
}
+
+ @Override
+ public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
+ return mSurfaceControlProxy;
+ }
}
private class TestListener implements DisplayAdapter.Listener {
public ArrayList<DisplayDevice> addedDisplays = new ArrayList<>();
public ArrayList<DisplayDevice> changedDisplays = new ArrayList<>();
+ public boolean traversalRequested = false;
@Override
public void onDisplayDeviceEvent(DisplayDevice device, int event) {
@@ -730,6 +860,8 @@
@Override
public void onTraversalRequested() {
+ traversalRequested = true;
}
}
+
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 6d40034..91b3cb7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -25,6 +25,7 @@
import static com.android.server.job.JobSchedulerService.RARE_INDEX;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
@@ -101,7 +102,7 @@
Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
JobSchedulerService.sUptimeMillisClock =
Clock.fixed(SystemClock.uptimeClock().instant(), ZoneOffset.UTC);
- JobSchedulerService.sElapsedRealtimeClock =
+ sElapsedRealtimeClock =
Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
}
@@ -204,7 +205,7 @@
@Test
public void testFraction() throws Exception {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long now = sElapsedRealtimeClock.millis();
assertEquals(1, createJobStatus(0, Long.MAX_VALUE).getFractionRunTime(), DELTA);
@@ -261,15 +262,15 @@
final JobStatus job = createJobStatus(jobInfo);
markImplicitConstraintsSatisfied(job, true);
- job.setChargingConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
- job.setChargingConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
markImplicitConstraintsSatisfied(job, false);
- job.setChargingConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
- job.setChargingConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
}
@@ -282,15 +283,15 @@
final JobStatus job = createJobStatus(jobInfo);
markImplicitConstraintsSatisfied(job, true);
- job.setIdleConstraintSatisfied(false);
+ job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE));
- job.setIdleConstraintSatisfied(true);
+ job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE));
markImplicitConstraintsSatisfied(job, false);
- job.setIdleConstraintSatisfied(false);
+ job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE));
- job.setIdleConstraintSatisfied(true);
+ job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE));
}
@@ -303,15 +304,15 @@
final JobStatus job = createJobStatus(jobInfo);
markImplicitConstraintsSatisfied(job, true);
- job.setBatteryNotLowConstraintSatisfied(false);
+ job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW));
- job.setBatteryNotLowConstraintSatisfied(true);
+ job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW));
markImplicitConstraintsSatisfied(job, false);
- job.setBatteryNotLowConstraintSatisfied(false);
+ job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW));
- job.setBatteryNotLowConstraintSatisfied(true);
+ job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW));
}
@@ -324,15 +325,15 @@
final JobStatus job = createJobStatus(jobInfo);
markImplicitConstraintsSatisfied(job, true);
- job.setStorageNotLowConstraintSatisfied(false);
+ job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW));
- job.setStorageNotLowConstraintSatisfied(true);
+ job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW));
markImplicitConstraintsSatisfied(job, false);
- job.setStorageNotLowConstraintSatisfied(false);
+ job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW));
- job.setStorageNotLowConstraintSatisfied(true);
+ job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW));
}
@@ -345,15 +346,15 @@
final JobStatus job = createJobStatus(jobInfo);
markImplicitConstraintsSatisfied(job, true);
- job.setTimingDelayConstraintSatisfied(false);
+ job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY));
- job.setTimingDelayConstraintSatisfied(true);
+ job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY));
markImplicitConstraintsSatisfied(job, false);
- job.setTimingDelayConstraintSatisfied(false);
+ job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY));
- job.setTimingDelayConstraintSatisfied(true);
+ job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY));
}
@@ -366,15 +367,15 @@
final JobStatus job = createJobStatus(jobInfo);
markImplicitConstraintsSatisfied(job, true);
- job.setDeadlineConstraintSatisfied(false);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
- job.setDeadlineConstraintSatisfied(true);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
markImplicitConstraintsSatisfied(job, false);
- job.setDeadlineConstraintSatisfied(false);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
- job.setDeadlineConstraintSatisfied(true);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
}
@@ -387,15 +388,15 @@
final JobStatus job = createJobStatus(jobInfo);
markImplicitConstraintsSatisfied(job, true);
- job.setConnectivityConstraintSatisfied(false);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
- job.setConnectivityConstraintSatisfied(true);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
markImplicitConstraintsSatisfied(job, false);
- job.setConnectivityConstraintSatisfied(false);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
- job.setConnectivityConstraintSatisfied(true);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
}
@@ -410,15 +411,15 @@
final JobStatus job = createJobStatus(jobInfo);
markImplicitConstraintsSatisfied(job, true);
- job.setContentTriggerConstraintSatisfied(false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
- job.setContentTriggerConstraintSatisfied(true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
markImplicitConstraintsSatisfied(job, false);
- job.setContentTriggerConstraintSatisfied(false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
- job.setContentTriggerConstraintSatisfied(true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
}
@@ -436,15 +437,15 @@
markImplicitConstraintsSatisfied(job, false);
- job.setChargingConstraintSatisfied(false);
- job.setConnectivityConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
- job.setChargingConstraintSatisfied(true);
- job.setConnectivityConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
// Still false because implicit constraints aren't satisfied.
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
@@ -452,61 +453,61 @@
markImplicitConstraintsSatisfied(job, true);
- job.setChargingConstraintSatisfied(false);
- job.setConnectivityConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
// Turn on constraints one at a time.
- job.setChargingConstraintSatisfied(true);
- job.setConnectivityConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
- job.setChargingConstraintSatisfied(false);
- job.setConnectivityConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
- job.setChargingConstraintSatisfied(false);
- job.setConnectivityConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
// With two of the 3 constraints satisfied (and implicit constraints also satisfied), only
// the unsatisfied constraint should return true.
- job.setChargingConstraintSatisfied(true);
- job.setConnectivityConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
- job.setChargingConstraintSatisfied(true);
- job.setConnectivityConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
- job.setChargingConstraintSatisfied(false);
- job.setConnectivityConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
- job.setChargingConstraintSatisfied(true);
- job.setConnectivityConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
@@ -526,15 +527,15 @@
markImplicitConstraintsSatisfied(job, false);
- job.setChargingConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(false);
- job.setDeadlineConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
- job.setChargingConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(true);
- job.setDeadlineConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
// Still false because implicit constraints aren't satisfied.
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
@@ -542,18 +543,18 @@
markImplicitConstraintsSatisfied(job, true);
- job.setChargingConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(false);
- job.setDeadlineConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
// Once implicit constraint are satisfied, deadline constraint should always return true.
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
// Turn on constraints one at a time.
- job.setChargingConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(false);
- job.setDeadlineConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
// Deadline should force isReady to be true, but isn't needed for the job to be
// considered ready.
@@ -561,17 +562,17 @@
// Once implicit constraint are satisfied, deadline constraint should always return true.
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
- job.setChargingConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(true);
- job.setDeadlineConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
// Once implicit constraint are satisfied, deadline constraint should always return true.
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
- job.setChargingConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(false);
- job.setDeadlineConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
// Since the deadline constraint is satisfied, none of the other explicit constraints are
// needed.
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
@@ -581,33 +582,33 @@
// With two of the 3 constraints satisfied (and implicit constraints also satisfied), only
// the unsatisfied constraint should return true.
- job.setChargingConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(true);
- job.setDeadlineConstraintSatisfied(false);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
// Once implicit constraint are satisfied, deadline constraint should always return true.
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
- job.setChargingConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(false);
- job.setDeadlineConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
// Once implicit constraint are satisfied, deadline constraint should always return true.
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
- job.setChargingConstraintSatisfied(false);
- job.setContentTriggerConstraintSatisfied(true);
- job.setDeadlineConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
// Once implicit constraint are satisfied, deadline constraint should always return true.
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
- job.setChargingConstraintSatisfied(true);
- job.setContentTriggerConstraintSatisfied(true);
- job.setDeadlineConstraintSatisfied(true);
+ job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
// Once implicit constraint are satisfied, deadline constraint should always return true.
@@ -621,15 +622,15 @@
new JobInfo.Builder(101, new ComponentName("foo", "bar")).build());
markImplicitConstraintsSatisfied(job, false);
- job.setDeviceNotDozingConstraintSatisfied(false, false);
+ job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), false, false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING));
- job.setDeviceNotDozingConstraintSatisfied(true, false);
+ job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), true, false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING));
markImplicitConstraintsSatisfied(job, true);
- job.setDeviceNotDozingConstraintSatisfied(false, false);
+ job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), false, false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING));
- job.setDeviceNotDozingConstraintSatisfied(true, false);
+ job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), true, false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING));
}
@@ -640,15 +641,15 @@
new JobInfo.Builder(101, new ComponentName("foo", "bar")).build());
markImplicitConstraintsSatisfied(job, false);
- job.setQuotaConstraintSatisfied(false);
+ job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA));
- job.setQuotaConstraintSatisfied(true);
+ job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA));
markImplicitConstraintsSatisfied(job, true);
- job.setQuotaConstraintSatisfied(false);
+ job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA));
- job.setQuotaConstraintSatisfied(true);
+ job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA));
}
@@ -659,22 +660,24 @@
new JobInfo.Builder(101, new ComponentName("foo", "bar")).build());
markImplicitConstraintsSatisfied(job, false);
- job.setBackgroundNotRestrictedConstraintSatisfied(false);
+ job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
- job.setBackgroundNotRestrictedConstraintSatisfied(true);
+ job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
markImplicitConstraintsSatisfied(job, true);
- job.setBackgroundNotRestrictedConstraintSatisfied(false);
+ job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
- job.setBackgroundNotRestrictedConstraintSatisfied(true);
+ job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
}
private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) {
- job.setQuotaConstraintSatisfied(isSatisfied);
- job.setDeviceNotDozingConstraintSatisfied(isSatisfied, false);
- job.setBackgroundNotRestrictedConstraintSatisfied(isSatisfied);
+ job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
+ job.setDeviceNotDozingConstraintSatisfied(
+ sElapsedRealtimeClock.millis(), isSatisfied, false);
+ job.setBackgroundNotRestrictedConstraintSatisfied(
+ sElapsedRealtimeClock.millis(), isSatisfied);
}
private static JobStatus createJobStatus(long earliestRunTimeElapsedMillis,
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 b72121f..bf95f4c 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
@@ -31,6 +31,7 @@
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.JobSchedulerService.sSystemClock;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -58,6 +59,7 @@
import android.app.IActivityManager;
import android.app.IUidObserver;
import android.app.job.JobInfo;
+import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -85,6 +87,7 @@
import com.android.server.job.JobStore;
import com.android.server.job.controllers.QuotaController.ExecutionStats;
import com.android.server.job.controllers.QuotaController.QcConstants;
+import com.android.server.job.controllers.QuotaController.ShrinkableDebits;
import com.android.server.job.controllers.QuotaController.TimingSession;
import com.android.server.usage.AppStandbyInternal;
@@ -125,6 +128,7 @@
private int mSourceUid;
private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener;
private IUidObserver mUidObserver;
+ private UsageStatsManagerInternal.UsageEventListener mUsageEventListener;
DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
private MockitoSession mMockingSession;
@@ -218,6 +222,8 @@
ArgumentCaptor.forClass(IUidObserver.class);
ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor =
ArgumentCaptor.forClass(PowerAllowlistInternal.TempAllowlistChangeListener.class);
+ ArgumentCaptor<UsageStatsManagerInternal.UsageEventListener> ueListenerCaptor =
+ ArgumentCaptor.forClass(UsageStatsManagerInternal.UsageEventListener.class);
mQuotaController = new QuotaController(mJobSchedulerService,
mock(BackgroundJobsController.class), mock(ConnectivityController.class));
@@ -229,6 +235,8 @@
verify(mPowerAllowlistInternal)
.registerTempAllowlistChangeListener(taChangeCaptor.capture());
mTempAllowlistListener = taChangeCaptor.getValue();
+ verify(mUsageStatsManager).registerListener(ueListenerCaptor.capture());
+ mUsageEventListener = ueListenerCaptor.getValue();
try {
verify(activityManager).registerUidObserver(
uidObserverCaptor.capture(),
@@ -288,7 +296,7 @@
verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid));
assertFalse(foregroundUids.get(uid));
}
- waitForQuietBackground();
+ waitForNonDelayedMessagesProcessed();
} catch (Exception e) {
fail("exception encountered: " + e.getMessage());
}
@@ -359,8 +367,9 @@
// Make sure tests aren't passing just because the default bucket is likely ACTIVE.
js.setStandbyBucket(FREQUENT_INDEX);
// Make sure Doze and background-not-restricted don't affect tests.
- js.setDeviceNotDozingConstraintSatisfied(/* state */ true, /* allowlisted */false);
- js.setBackgroundNotRestrictedConstraintSatisfied(true);
+ js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(),
+ /* state */ true, /* allowlisted */false);
+ js.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
return js;
}
@@ -384,13 +393,8 @@
}
}
- private void waitForQuietBackground() throws Exception {
- for (int i = 0; i < 5; ++i) {
- if (!mQuotaController.isActiveBackgroundProcessing()) {
- break;
- }
- Thread.sleep(500);
- }
+ private void waitForNonDelayedMessagesProcessed() {
+ mQuotaController.getHandler().runWithScissors(() -> {}, 15_000);
}
@Test
@@ -1329,7 +1333,9 @@
timeUsedMs, 5), true);
JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0);
setStandbyBucket(RARE_INDEX, job);
- mQuotaController.maybeStartTrackingJobLocked(job, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job, null);
+ }
setCharging();
synchronized (mQuotaController.mLock) {
@@ -5157,4 +5163,257 @@
eq(10 * SECOND_IN_MILLIS));
verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs));
}
+
+ @Test
+ public void testEJDebitTallying() {
+ setStandbyBucket(RARE_INDEX);
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+ // 15 seconds for each 30 second chunk.
+ setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS);
+
+ // No history. Debits should be 0.
+ ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Regular job shouldn't affect EJ tally.
+ JobStatus regJob = createJobStatus("testEJDebitTallying", 1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(regJob, null);
+ mQuotaController.prepareForExecutionLocked(regJob);
+ }
+ advanceElapsedClock(5000);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(regJob, null, false);
+ }
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // EJ job should affect EJ tally.
+ JobStatus eJob = createExpeditedJobStatus("testEJDebitTallying", 2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(eJob, null);
+ mQuotaController.prepareForExecutionLocked(eJob);
+ }
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(eJob, null, false);
+ }
+ assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+ assertEquals(5 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Instantaneous event for a different user shouldn't affect tally.
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS);
+
+ UsageEvents.Event event =
+ new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+ event.mPackage = SOURCE_PACKAGE;
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID + 10, event);
+ assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+
+ // Instantaneous event for correct user should reduce tally.
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+ waitForNonDelayedMessagesProcessed();
+ assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+ assertEquals(6 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Activity start shouldn't reduce tally, but duration with activity started should affect
+ // remaining EJ time.
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis());
+ event.mPackage = SOURCE_PACKAGE;
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+ waitForNonDelayedMessagesProcessed();
+ advanceElapsedClock(30 * SECOND_IN_MILLIS);
+ assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+ assertEquals(6 * MINUTE_IN_MILLIS + 15 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ advanceElapsedClock(30 * SECOND_IN_MILLIS);
+ assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+ assertEquals(6 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // With activity pausing/stopping/destroying, tally should be updated.
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis());
+ event.mPackage = SOURCE_PACKAGE;
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+ waitForNonDelayedMessagesProcessed();
+ assertEquals(3 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ @Test
+ public void testEJDebitTallying_StaleSession() {
+ setStandbyBucket(RARE_INDEX);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ TimingSession ts = new TimingSession(nowElapsed, nowElapsed + 10 * MINUTE_IN_MILLIS, 5);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true);
+
+ // Make the session stale.
+ advanceElapsedClock(12 * MINUTE_IN_MILLIS + mQcConstants.EJ_WINDOW_SIZE_MS);
+
+ // With lazy deletion, we don't update the tally until getRemainingEJExecutionTimeLocked()
+ // is called, so call that first.
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+ assertEquals(0, debit.getTallyLocked());
+ }
+
+ /**
+ * Tests that rewards are properly accounted when there's no EJ running and the rewards exceed
+ * the accumulated debits.
+ */
+ @Test
+ public void testEJDebitTallying_RewardExceedDebits_NoActiveSession() {
+ setStandbyBucket(WORKING_INDEX);
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS);
+
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS,
+ nowElapsed - 4 * MINUTE_IN_MILLIS, 2);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true);
+
+ ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+ assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked());
+ assertEquals(29 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(30 * SECOND_IN_MILLIS);
+ UsageEvents.Event event =
+ new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+ event.mPackage = SOURCE_PACKAGE;
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+ waitForNonDelayedMessagesProcessed();
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(30 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(30 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Excessive rewards don't increase maximum quota.
+ event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+ event.mPackage = SOURCE_PACKAGE;
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+ waitForNonDelayedMessagesProcessed();
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(30 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
+ * Tests that rewards are properly accounted when there's an active EJ running and the rewards
+ * exceed the accumulated debits.
+ */
+ @Test
+ public void testEJDebitTallying_RewardExceedDebits_ActiveSession() {
+ setStandbyBucket(WORKING_INDEX);
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS);
+ // 15 seconds for each 30 second chunk.
+ setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS);
+
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS,
+ nowElapsed - 4 * MINUTE_IN_MILLIS, 2);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true);
+
+ ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+ assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked());
+ assertEquals(29 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // With rewards coming in while an EJ is running, the remaining execution time should be
+ // adjusted accordingly (decrease due to EJ running + increase from reward).
+ JobStatus eJob =
+ createExpeditedJobStatus("testEJDebitTallying_RewardExceedDebits_ActiveSession", 1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(eJob, null);
+ mQuotaController.prepareForExecutionLocked(eJob);
+ }
+ advanceElapsedClock(30 * SECOND_IN_MILLIS);
+ assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked());
+ assertEquals(28 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(30 * SECOND_IN_MILLIS);
+ UsageEvents.Event event =
+ new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+ event.mPackage = SOURCE_PACKAGE;
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+ waitForNonDelayedMessagesProcessed();
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(29 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(28 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Activity start shouldn't reduce tally, but duration with activity started should affect
+ // remaining EJ time.
+ event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis());
+ event.mPackage = SOURCE_PACKAGE;
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+ waitForNonDelayedMessagesProcessed();
+ advanceElapsedClock(30 * SECOND_IN_MILLIS);
+ assertEquals(0, debit.getTallyLocked());
+ // Decrease by 30 seconds for running EJ, increase by 15 seconds due to ongoing activity.
+ assertEquals(27 * MINUTE_IN_MILLIS + 45 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ advanceElapsedClock(30 * SECOND_IN_MILLIS);
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(27 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+ event.mPackage = SOURCE_PACKAGE;
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+ waitForNonDelayedMessagesProcessed();
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(28 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // At this point, with activity pausing/stopping/destroying, since we're giving a reward,
+ // tally should remain 0, and time remaining shouldn't change since it was accounted for
+ // at every step.
+ event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis());
+ event.mPackage = SOURCE_PACKAGE;
+ mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+ waitForNonDelayedMessagesProcessed();
+ assertEquals(0, debit.getTallyLocked());
+ assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 66b037d..68d6557 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -123,9 +123,10 @@
.setPowerUsage(POWER_USAGE_HIGH)
.setAccuracy(ProviderProperties.ACCURACY_FINE)
.build();
+ private static final CallerIdentity PROVIDER_IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
+ "mypackage", "attribution");
private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
- "mypackage",
- "attribution");
+ "mypackage", "attribution", "listener");
private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid());
private Random mRandom;
@@ -169,7 +170,7 @@
mPassive.startManager();
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
- mProvider = new TestProvider(PROPERTIES, IDENTITY);
+ mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
mProvider.setProviderAllowed(true);
mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive);
@@ -351,7 +352,7 @@
@Test
public void testGetLastLocation_ClearOnMockRemoval() {
- MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, IDENTITY);
+ MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY);
mockProvider.setAllowed(true);
mManager.setMockProvider(mockProvider);
@@ -441,7 +442,7 @@
@Test
public void testRegisterListener_SameProcess() throws Exception {
CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
- "attribution");
+ "attribution", "listener");
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
@@ -477,7 +478,7 @@
@Test
public void testRegisterListener_Unregister_SameProcess() throws Exception {
CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
- "attribution");
+ "attribution", "listener");
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
@@ -604,7 +605,7 @@
@Test
public void testRegisterListener_Wakelock() throws Exception {
CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
- "attribution");
+ "attribution", "listener");
ILocationListener listener = createMockLocationListener();
mManager.registerLocationRequest(
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 1328b91..07f6732 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -110,6 +110,8 @@
UserInfo userInfo = addUser(USER_ID_1);
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
+
+ mAppHibernationService.mIsServiceEnabled = true;
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
index 41e1563..6630178 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
@@ -66,11 +66,13 @@
inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
.times(1)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION),
eq(Process.myUid()), eq(getContext().getPackageName()),
- eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED));
+ eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
+ eq(AppOpsManager.MODE_ALLOWED));
inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
.times(1)).onOpNoted(eq(AppOpsManager.OP_CAMERA),
eq(Process.myUid()), eq(getContext().getPackageName()),
- eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED));
+ eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
+ eq(AppOpsManager.MODE_ALLOWED));
// Stop watching
appOpsManager.stopWatchingNoted(listener);
@@ -94,7 +96,8 @@
verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
.times(2)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION),
eq(Process.myUid()), eq(getContext().getPackageName()),
- eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED));
+ eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
+ eq(AppOpsManager.MODE_ALLOWED));
// Finish up
appOpsManager.stopWatchingNoted(listener);
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java
index fec8aa9..c12eb32 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java
@@ -63,11 +63,13 @@
inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
.times(1)).onOpStarted(eq(AppOpsManager.OP_FINE_LOCATION),
eq(Process.myUid()), eq(getContext().getPackageName()),
- eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED));
+ eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
+ eq(AppOpsManager.MODE_ALLOWED));
inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
.times(1)).onOpStarted(eq(AppOpsManager.OP_CAMERA),
eq(Process.myUid()), eq(getContext().getPackageName()),
- eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED));
+ eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
+ eq(AppOpsManager.MODE_ALLOWED));
// Stop watching
appOpsManager.stopWatchingStarted(listener);
@@ -91,7 +93,8 @@
verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
.times(2)).onOpStarted(eq(AppOpsManager.OP_CAMERA),
eq(Process.myUid()), eq(getContext().getPackageName()),
- eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED));
+ eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
+ eq(AppOpsManager.MODE_ALLOWED));
verifyNoMoreInteractions(listener);
// Finish up
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
new file mode 100644
index 0000000..4308885
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+package com.android.server.appsearch.external.localstorage.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class AppSearchStatsTest {
+ static final String TEST_PACKAGE_NAME = "com.google.test";
+ static final String TEST_DATA_BASE = "testDataBase";
+ static final int TEST_STATUS_CODE = 2;
+ static final int TEST_TOTAL_LATENCY_MILLIS = 20;
+
+ @Test
+ public void testAppSearchStats_GeneralStats() {
+ final GeneralStats gStats =
+ new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
+ .setStatusCode(TEST_STATUS_CODE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+ .build();
+
+ assertThat(gStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+ assertThat(gStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
+ assertThat(gStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+ assertThat(gStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+ }
+
+ @Test
+ public void testAppSearchStats_CallStats() {
+ final int estimatedBinderLatencyMillis = 1;
+ final int numOperationsSucceeded = 2;
+ final int numOperationsFailed = 3;
+
+ final GeneralStats gStats =
+ new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
+ .setStatusCode(TEST_STATUS_CODE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+ .build();
+ final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS;
+ final CallStats cStats =
+ new CallStats.Builder(gStats)
+ .setCallType(callType)
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
+ .setNumOperationsSucceeded(numOperationsSucceeded)
+ .setNumOperationsFailed(numOperationsFailed)
+ .build();
+
+ assertThat(cStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+ assertThat(cStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE);
+ assertThat(cStats.getGeneralStats().getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+ assertThat(cStats.getGeneralStats().getTotalLatencyMillis())
+ .isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+ assertThat(cStats.getEstimatedBinderLatencyMillis())
+ .isEqualTo(estimatedBinderLatencyMillis);
+ assertThat(cStats.getCallType()).isEqualTo(callType);
+ assertThat(cStats.getNumOperationsSucceeded()).isEqualTo(numOperationsSucceeded);
+ assertThat(cStats.getNumOperationsFailed()).isEqualTo(numOperationsFailed);
+ }
+
+ @Test
+ public void testAppSearchStats_PutDocumentStats() {
+ final int generateDocumentProtoLatencyMillis = 1;
+ final int rewriteDocumentTypesLatencyMillis = 2;
+ final int nativeLatencyMillis = 3;
+ final int nativeDocumentStoreLatencyMillis = 4;
+ final int nativeIndexLatencyMillis = 5;
+ final int nativeIndexMergeLatencyMillis = 6;
+ final int nativeDocumentSize = 7;
+ final int nativeNumTokensIndexed = 8;
+ final int nativeNumTokensClipped = 9;
+
+ final GeneralStats gStats =
+ new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
+ .setStatusCode(TEST_STATUS_CODE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+ .build();
+ final PutDocumentStats pStats =
+ new PutDocumentStats.Builder(gStats)
+ .setGenerateDocumentProtoLatencyMillis(generateDocumentProtoLatencyMillis)
+ .setRewriteDocumentTypesLatencyMillis(rewriteDocumentTypesLatencyMillis)
+ .setNativeLatencyMillis(nativeLatencyMillis)
+ .setNativeDocumentStoreLatencyMillis(nativeDocumentStoreLatencyMillis)
+ .setNativeIndexLatencyMillis(nativeIndexLatencyMillis)
+ .setNativeIndexMergeLatencyMillis(nativeIndexMergeLatencyMillis)
+ .setNativeDocumentSizeBytes(nativeDocumentSize)
+ .setNativeNumTokensIndexed(nativeNumTokensIndexed)
+ .setNativeNumTokensClipped(nativeNumTokensClipped)
+ .build();
+
+ assertThat(pStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+ assertThat(pStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE);
+ assertThat(pStats.getGeneralStats().getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+ assertThat(pStats.getGeneralStats().getTotalLatencyMillis())
+ .isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+ assertThat(pStats.getGenerateDocumentProtoLatencyMillis())
+ .isEqualTo(generateDocumentProtoLatencyMillis);
+ assertThat(pStats.getRewriteDocumentTypesLatencyMillis())
+ .isEqualTo(rewriteDocumentTypesLatencyMillis);
+ assertThat(pStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(pStats.getNativeDocumentStoreLatencyMillis())
+ .isEqualTo(nativeDocumentStoreLatencyMillis);
+ assertThat(pStats.getNativeIndexLatencyMillis()).isEqualTo(nativeIndexLatencyMillis);
+ assertThat(pStats.getNativeIndexMergeLatencyMillis())
+ .isEqualTo(nativeIndexMergeLatencyMillis);
+ assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize);
+ assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed);
+ assertThat(pStats.getNativeNumTokensClipped()).isEqualTo(nativeNumTokensClipped);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index 2cbc3f3..a694d5e 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -153,4 +153,25 @@
assertEquals("Period not restored", periodic.periodMillis, oneoff.periodMillis);
assertEquals("Flex not restored", periodic.flexMillis, oneoff.flexMillis);
}
+
+ @SmallTest
+ public void testScheduleAsEjIsInExtras() {
+ Account account1 = new Account("account1", "type1");
+ Bundle b1 = new Bundle();
+ b1.putBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true);
+
+ SyncOperation op1 = new SyncOperation(account1, 0, 1, "foo", 0,
+ SyncOperation.REASON_USER_START, "authority1", b1, false,
+ ContentResolver.SYNC_EXEMPTION_NONE);
+ assertTrue(op1.isScheduledAsExpeditedJob());
+
+ PersistableBundle pb = op1.toJobInfoExtras();
+ assertTrue("EJ extra not found in job extras",
+ ((PersistableBundle) pb.get("syncExtras"))
+ .containsKey(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB));
+
+ SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb);
+ assertTrue("EJ extra not found in extras", op2.getClonedExtras()
+ .getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 1c55072..bc86d1d 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -993,7 +993,7 @@
private DisplayDeviceInfo mDisplayDeviceInfo;
FakeDisplayDevice() {
- super(null, null, "");
+ super(null, null, "", mContext);
}
public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
index f49cbca..9bf95c0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
@@ -221,7 +221,8 @@
"testDeviceSelect");
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH);
+ assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_ON);
mTestLooper.dispatchAll();
@@ -238,12 +239,15 @@
DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/false);
action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_STANDBY);
mTestLooper.dispatchAll();
HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed(
ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER);
assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed);
+ mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.processCommand(REPORT_POWER_STATUS_ON);
@@ -261,6 +265,9 @@
DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/false);
action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_STANDBY);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
@@ -285,6 +292,9 @@
DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/false);
action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_STANDBY);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
@@ -330,8 +340,11 @@
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@@ -354,9 +367,12 @@
HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed(
ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(userControlPressed);
+ mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 462f3e3..32a7048 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -485,17 +485,6 @@
}
@Test
- public void getCecVersion_default() {
- // Set the Settings value to "null" to emulate it being empty and force the default value.
- Settings.Global.putString(mContextSpy.getContentResolver(),
- Settings.Global.HDMI_CEC_VERSION,
- null);
- mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
- assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
- HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
- }
-
- @Test
public void getCecVersion_1_4() {
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
index 3e9709d..f0a9a00 100644
--- a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
@@ -31,6 +31,7 @@
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
+import android.hardware.lights.SystemLightsManager;
import android.os.Looper;
import androidx.test.filters.SmallTest;
@@ -93,7 +94,7 @@
@Test
public void testGetLights_filtersSystemLights() {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
// When lights are listed, only the 4 MICROPHONE lights should be visible.
assertThat(manager.getLights().size()).isEqualTo(4);
@@ -102,14 +103,14 @@
@Test
public void testControlMultipleLights() {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
// When the session requests to turn 3/4 lights on:
LightsManager.LightsSession session = manager.openSession();
session.requestLights(new Builder()
- .setLight(manager.getLights().get(0), new LightState(0xf1))
- .setLight(manager.getLights().get(1), new LightState(0xf2))
- .setLight(manager.getLights().get(2), new LightState(0xf3))
+ .addLight(manager.getLights().get(0), new LightState(0xf1))
+ .addLight(manager.getLights().get(1), new LightState(0xf2))
+ .addLight(manager.getLights().get(2), new LightState(0xf3))
.build());
// Then all 3 should turn on.
@@ -122,9 +123,9 @@
}
@Test
- public void testControlLights_onlyEffectiveForLifetimeOfClient() {
+ public void testControlLights_onlyEffectiveForLifetimeOfClient() throws Exception {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
Light micLight = manager.getLights().get(0);
// The light should begin by being off.
@@ -132,38 +133,41 @@
// When a session commits changes:
LightsManager.LightsSession session = manager.openSession();
- session.requestLights(new Builder().setLight(micLight, new LightState(GREEN)).build());
+ session.requestLights(new Builder().addLight(micLight, new LightState(GREEN)).build());
// Then the light should turn on.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(GREEN);
// When the session goes away:
session.close();
+
// Then the light should turn off.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(TRANSPARENT);
}
@Test
- public void testControlLights_firstCallerWinsContention() {
+ public void testControlLights_firstCallerWinsContention() throws Exception {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
Light micLight = manager.getLights().get(0);
LightsManager.LightsSession session1 = manager.openSession();
LightsManager.LightsSession session2 = manager.openSession();
// When session1 and session2 both request the same light:
- session1.requestLights(new Builder().setLight(micLight, new LightState(BLUE)).build());
- session2.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build());
+ session1.requestLights(new Builder().addLight(micLight, new LightState(BLUE)).build());
+ session2.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build());
// Then session1 should win because it was created first.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLUE);
// When session1 goes away:
session1.close();
+
// Then session2 should have its request go into effect.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(WHITE);
// When session2 goes away:
session2.close();
+
// Then the light should turn off because there are no more sessions.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0);
}
@@ -171,12 +175,12 @@
@Test
public void testClearLight() {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
Light micLight = manager.getLights().get(0);
// When the session turns a light on:
LightsManager.LightsSession session = manager.openSession();
- session.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build());
+ session.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build());
// And then the session clears it again:
session.requestLights(new Builder().clearLight(micLight).build());
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index ff43da6..ee0a16a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -86,6 +86,7 @@
private TestData mBarUser0DelegateLastClassLoader;
private TestData mSystemServerJar;
+ private TestData mSystemServerJarUpdatedContext;
private TestData mSystemServerJarInvalid;
private int mUser0;
@@ -113,6 +114,8 @@
mSystemServerJar = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME);
mSystemServerJarInvalid = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME);
+ mSystemServerJarUpdatedContext = new TestData("android", isa, mUser0,
+ DELEGATE_LAST_CLASS_LOADER_NAME);
mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null,
mInstaller, mInstallLock);
@@ -522,6 +525,24 @@
}
@Test
+ public void testSystemServerOverwritesContext() {
+ // Record bar secondaries with the default PathClassLoader.
+ List<String> secondaries = mSystemServerJar.getSecondaryDexPaths();
+
+ notifyDexLoad(mSystemServerJar, secondaries, mUser0);
+ PackageUseInfo pui = getPackageUseInfo(mSystemServerJar);
+ assertSecondaryUse(mSystemServerJar, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+
+ // Record bar secondaries again with a different class loader. This will change the context.
+ notifyDexLoad(mSystemServerJarUpdatedContext, secondaries, mUser0);
+
+ pui = getPackageUseInfo(mSystemServerJar);
+ // We expect that all the contexts to be updated according to the last notify.
+ assertSecondaryUse(mSystemServerJarUpdatedContext, pui, secondaries,
+ /*isUsedByOtherApps*/false, mUser0);
+ }
+
+ @Test
public void testNotifyUnsupportedClassLoaderDoesNotChangeExisting() {
List<String> secondaries = mBarUser0.getSecondaryDexPaths();
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/OWNERS b/services/tests/servicestests/src/com/android/server/pm/dex/OWNERS
new file mode 100644
index 0000000..66ef75d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/dex/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index adf4551..3450710 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -451,7 +451,7 @@
"PCL[new_context.dex]");
assertTrue(record(fooSecondary1User0NewContext));
- // Not check that the context was switch to variable.
+ // Now check that the context was switch to variable.
TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext(
PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT);
@@ -461,6 +461,22 @@
}
@Test
+ public void testRecordClassLoaderContextOverwritten() {
+ // Record a secondary dex file.
+ assertTrue(record(mFooSecondary1User0));
+ // Now update its context.
+ TestData fooSecondary1User0NewContext = mFooSecondary1User0.updateClassLoaderContext(
+ "PCL[new_context.dex]", true);
+ assertTrue(record(fooSecondary1User0NewContext));
+
+ // Now check that the context was overwritten.
+ TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext(
+ "PCL[new_context.dex]", true);
+
+ assertPackageDexUsage(null, expectedContext);
+ }
+
+ @Test
public void testDexUsageClassLoaderContext() {
final boolean isUsedByOtherApps = false;
final int userId = 0;
@@ -642,8 +658,9 @@
private boolean record(TestData testData) {
return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile,
- testData.mOwnerUserId, testData.mLoaderIsa,
- testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext);
+ testData.mOwnerUserId, testData.mLoaderIsa,
+ testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext,
+ testData.mOverwriteCLC);
}
private boolean record(PackageDexUsage packageDexUsage, TestData testData, Set<String> users) {
@@ -651,7 +668,8 @@
for (String user : users) {
result = result && packageDexUsage.record(testData.mPackageName, testData.mDexFile,
testData.mOwnerUserId, testData.mLoaderIsa,
- testData.mPrimaryOrSplit, user, testData.mClassLoaderContext);
+ testData.mPrimaryOrSplit, user, testData.mClassLoaderContext,
+ testData.mOverwriteCLC);
}
return result;
}
@@ -682,15 +700,16 @@
private final boolean mPrimaryOrSplit;
private final String mUsedBy;
private final String mClassLoaderContext;
+ private final boolean mOverwriteCLC;
private TestData(String packageName, String dexFile, int ownerUserId,
String loaderIsa, boolean primaryOrSplit, String usedBy) {
this(packageName, dexFile, ownerUserId, loaderIsa, primaryOrSplit,
- usedBy, "PCL[" + dexFile + "]");
+ usedBy, "PCL[" + dexFile + "]", false);
}
private TestData(String packageName, String dexFile, int ownerUserId,
String loaderIsa, boolean primaryOrSplit, String usedBy,
- String classLoaderContext) {
+ String classLoaderContext, boolean overwriteCLC) {
mPackageName = packageName;
mDexFile = dexFile;
mOwnerUserId = ownerUserId;
@@ -698,16 +717,21 @@
mPrimaryOrSplit = primaryOrSplit;
mUsedBy = usedBy;
mClassLoaderContext = classLoaderContext;
+ mOverwriteCLC = overwriteCLC;
}
private TestData updateClassLoaderContext(String newContext) {
+ return updateClassLoaderContext(newContext, mOverwriteCLC);
+ }
+
+ private TestData updateClassLoaderContext(String newContext, boolean overwriteCLC) {
return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa,
- mPrimaryOrSplit, mUsedBy, newContext);
+ mPrimaryOrSplit, mUsedBy, newContext, overwriteCLC);
}
private TestData updateUsedBy(String newUsedBy) {
return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa,
- mPrimaryOrSplit, newUsedBy, mClassLoaderContext);
+ mPrimaryOrSplit, newUsedBy, mClassLoaderContext, mOverwriteCLC);
}
private boolean isUsedByOtherApps() {
diff --git a/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java
new file mode 100644
index 0000000..ef20ee7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.power;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorManager;
+import android.testing.TestableContext;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.display.TestUtils;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Constructor;
+import java.time.Duration;
+
+public class FaceDownDetectorTest {
+ @ClassRule
+ public static final TestableContext sContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ private final FaceDownDetector mFaceDownDetector =
+ new FaceDownDetector(this::onFlip);
+
+ @Mock private SensorManager mSensorManager;
+
+ private long mCurrentTime;
+ private int mOnFaceDownCalls = 0;
+ private int mOnFaceDownExitCalls = 0;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ sContext.addMockSystemService(SensorManager.class, mSensorManager);
+ mCurrentTime = 0;
+ }
+
+ @Test
+ public void faceDownFor2Seconds_triggersFaceDown() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+
+ // Face up
+ // Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
+
+ for (int i = 0; i < 200; i++) {
+ advanceTime(Duration.ofMillis(20));
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
+ }
+
+ assertThat(mOnFaceDownCalls).isEqualTo(1);
+ assertThat(mOnFaceDownExitCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void faceDownFor2Seconds_withMotion_DoesNotTriggerFaceDown() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+
+ // Face up
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
+
+ for (int i = 0; i < 100; i++) {
+ advanceTime(Duration.ofMillis(20));
+ //Move along x direction
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f * i, 0.0f, -10.0f));
+ }
+
+ assertThat(mOnFaceDownCalls).isEqualTo(0);
+ assertThat(mOnFaceDownExitCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void faceDownForHalfSecond_DoesNotTriggerFaceDown() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+
+ // Face up
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
+
+ for (int i = 0; i < 100; i++) {
+ advanceTime(Duration.ofMillis(5));
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
+ }
+
+ assertThat(mOnFaceDownCalls).isEqualTo(0);
+ assertThat(mOnFaceDownExitCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void faceDownFor2Seconds_followedByFaceUp_triggersFaceDownExit() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+
+ // Face up
+ // Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
+
+ // Trigger face down
+ for (int i = 0; i < 100; i++) {
+ advanceTime(Duration.ofMillis(20));
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
+ }
+
+ // Phone flips
+ for (int i = 0; i < 10; i++) {
+ advanceTime(Duration.ofMillis(5));
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 1.0f, 0.0f));
+ }
+
+ assertThat(mOnFaceDownCalls).isEqualTo(1);
+ assertThat(mOnFaceDownExitCalls).isEqualTo(1);
+ }
+
+ private void advanceTime(Duration duration) {
+ mCurrentTime += duration.toNanos();
+ }
+
+ /**
+ * Create a test event to replicate an accelerometer sensor event.
+ * @param x Acceleration along the x dimension.
+ * @param y Acceleration along the y dimension.
+ * @param gravity Acceleration along the Z dimension. Relates to
+ */
+ private SensorEvent createTestEvent(float x, float y, float gravity) throws Exception {
+ final Constructor<SensorEvent> constructor =
+ SensorEvent.class.getDeclaredConstructor(int.class);
+ constructor.setAccessible(true);
+ final SensorEvent event = constructor.newInstance(3);
+ event.sensor =
+ TestUtils.createSensor(Sensor.TYPE_ACCELEROMETER, Sensor.STRING_TYPE_ACCELEROMETER);
+ event.values[0] = x;
+ event.values[1] = y;
+ event.values[2] = gravity;
+ event.timestamp = mCurrentTime;
+ return event;
+ }
+
+ private void onFlip(boolean isFaceDown) {
+ if (isFaceDown) {
+ mOnFaceDownCalls++;
+ } else {
+ mOnFaceDownExitCalls++;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
index 1c2d8e8..5012ca9 100644
--- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
@@ -209,7 +209,8 @@
private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
- SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
+ FaceDownDetector faceDownDetector) {
return mNotifierMock;
}
@@ -296,6 +297,7 @@
IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME)),
mInjector.createSuspendBlocker(mService, "testBlocker"),
+ null,
null);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 592eea1..ea27331 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -98,9 +98,11 @@
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Tests for {@link com.android.server.power.PowerManagerService}.
@@ -211,7 +213,8 @@
mService = new PowerManagerService(mContextSpy, new Injector() {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
- SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
+ FaceDownDetector faceDownDetector) {
return mNotifierMock;
}
@@ -401,30 +404,33 @@
@Test
public void testGetDesiredScreenPolicy_WithVR() throws Exception {
createService();
+ mService.systemReady(null);
// Brighten up the screen
- mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0);
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
+ null, null);
+ assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
// Move to VR
mService.setVrModeEnabled(true);
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
DisplayPowerRequest.POLICY_VR);
// Then take a nap
- mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
- 0);
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0,
+ null, null);
+ assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
DisplayPowerRequest.POLICY_OFF);
// Wake up to VR
- mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0);
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
+ null, null);
+ assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
DisplayPowerRequest.POLICY_VR);
// And back to normal
mService.setVrModeEnabled(false);
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
}
@@ -675,8 +681,9 @@
}
@Test
- public void testForceSuspend_forceSuspendFailurePropogated() {
+ public void testForceSuspend_forceSuspendFailurePropagated() throws Exception {
createService();
+ startSystem();
when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false);
assertThat(mService.getBinderServiceInstance().forceSuspend()).isFalse();
}
@@ -840,7 +847,7 @@
createService();
startSystem();
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
}
@@ -859,11 +866,8 @@
public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception {
when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
createService();
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
- DisplayPowerRequest.POLICY_OFF);
-
startSystem();
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
DisplayPowerRequest.POLICY_OFF);
}
@@ -873,7 +877,7 @@
createService();
startSystem();
forceAwake();
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
}
@@ -889,7 +893,7 @@
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
- assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
}
@@ -1163,6 +1167,87 @@
}
@Test
+ public void testMultiDisplay_wakefulnessUpdates() throws Exception {
+ final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ listener.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+
+ createService();
+ startSystem();
+ listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0,
+ null, null);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, 0, 0, 0, 0,
+ null, null);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+
+ mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
+ null, null);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ @Test
+ public void testMultiDisplay_addDisplayGroup_preservesWakefulness() throws Exception {
+ final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ listener.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+
+ createService();
+ startSystem();
+
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0,
+ null, null);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+
+ listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+ }
+
+ @Test
+ public void testMultiDisplay_removeDisplayGroup_updatesWakefulness() throws Exception {
+ final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ listener.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+
+ createService();
+ startSystem();
+ listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0,
+ null, null);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ listener.get().onDisplayGroupRemoved(nonDefaultDisplayGroupId);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+
+ mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
+ null, null);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ @Test
public void testGetFullPowerSavePolicy_returnsStateMachineResult() {
createService();
mService.systemReady(null);
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 5574836..5f86d28 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -287,8 +287,7 @@
}
private static ExternalTimeSuggestion createExternalTimeSuggestion() {
- TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
- return new ExternalTimeSuggestion(timeValue);
+ return new ExternalTimeSuggestion(100L, 1_000_000L);
}
private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index daa1b25..f7a498b 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -1483,11 +1483,8 @@
* reference time.
*/
ExternalTimeSuggestion generateExternalTimeSuggestion(Instant suggestedTime) {
- TimestampedValue<Long> utcTime =
- new TimestampedValue<>(
- mFakeEnvironment.peekElapsedRealtimeMillis(),
+ return new ExternalTimeSuggestion(mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
- return new ExternalTimeSuggestion(utcTime);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
index b40d59c..57e95d7 100644
--- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
@@ -22,6 +22,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -619,6 +620,129 @@
}
@Test
+ public void testWatchedLongSparseArray() {
+ final String name = "WatchedLongSparseArray";
+ WatchableTester tester;
+
+ // Create a few leaves
+ Leaf leafA = new Leaf();
+ Leaf leafB = new Leaf();
+ Leaf leafC = new Leaf();
+ Leaf leafD = new Leaf();
+
+ // Test WatchedLongSparseArray
+ WatchedLongSparseArray<Leaf> array = new WatchedLongSparseArray<>();
+ array.put(INDEX_A, leafA);
+ array.put(INDEX_B, leafB);
+ tester = new WatchableTester(array, name);
+ tester.verify(0, "Initial array - no registration");
+ leafA.tick();
+ tester.verify(0, "Updates with no registration");
+ tester.register();
+ tester.verify(0, "Updates with no registration");
+ leafA.tick();
+ tester.verify(1, "Updates with registration");
+ leafB.tick();
+ tester.verify(2, "Updates with registration");
+ array.remove(INDEX_B);
+ tester.verify(3, "Removed b");
+ leafB.tick();
+ tester.verify(3, "Updates with b not watched");
+ array.put(INDEX_B, leafB);
+ array.put(INDEX_C, leafB);
+ tester.verify(5, "Added b twice");
+ leafB.tick();
+ tester.verify(6, "Changed b - single notification");
+ array.remove(INDEX_C);
+ tester.verify(7, "Removed first b");
+ leafB.tick();
+ tester.verify(8, "Changed b - single notification");
+ array.remove(INDEX_B);
+ tester.verify(9, "Removed second b");
+ leafB.tick();
+ tester.verify(9, "Updated leafB - no change");
+ array.clear();
+ tester.verify(10, "Cleared array");
+ leafB.tick();
+ tester.verify(10, "Change to b not in array");
+
+ // Special methods
+ array.put(INDEX_A, leafA);
+ array.put(INDEX_B, leafB);
+ array.put(INDEX_C, leafC);
+ tester.verify(13, "Added c");
+ leafC.tick();
+ tester.verify(14, "Ticked c");
+ array.setValueAt(array.indexOfKey(INDEX_C), leafD);
+ tester.verify(15, "Replaced c with d");
+ leafC.tick();
+ tester.verify(15, "Ticked c (c not registered)");
+ leafD.tick();
+ tester.verify(16, "Ticked d and c (c not registered)");
+ array.append(INDEX_D, leafC);
+ tester.verify(17, "Append c");
+ leafC.tick();
+ leafD.tick();
+ tester.verify(19, "Ticked d and c");
+ assertEquals("Verify four elements", 4, array.size());
+ // Figure out which elements are at which indices.
+ Leaf[] x = new Leaf[4];
+ for (int i = 0; i < 4; i++) {
+ x[i] = array.valueAt(i);
+ }
+ array.removeAt(1);
+ tester.verify(20, "Removed one element");
+ x[1].tick();
+ tester.verify(20, "Ticked one removed element");
+ x[2].tick();
+ tester.verify(21, "Ticked one remaining element");
+
+ // Snapshot
+ {
+ final WatchedLongSparseArray<Leaf> arraySnap = array.snapshot();
+ tester.verify(21, "Generate snapshot (no changes)");
+ // Verify that the snapshot is a proper copy of the source.
+ assertEquals(name + " snap same size",
+ array.size(), arraySnap.size());
+ for (int i = 0; i < array.size(); i++) {
+ for (int j = 0; j < arraySnap.size(); j++) {
+ assertTrue(name + " elements differ",
+ array.valueAt(i) != arraySnap.valueAt(j));
+ }
+ assertTrue(name + " element copy",
+ array.valueAt(i).equals(arraySnap.valueAt(i)));
+ }
+ leafD.tick();
+ tester.verify(22, "Tick after snapshot");
+ // Verify that the array snapshot is sealed
+ verifySealed(name, ()->arraySnap.put(INDEX_A, leafB));
+ assertTrue(!array.isSealed());
+ assertTrue(arraySnap.isSealed());
+ }
+ // Recreate the snapshot since the test corrupted it.
+ {
+ final WatchedLongSparseArray<Leaf> arraySnap = array.snapshot();
+ // Verify that elements are also snapshots
+ final Leaf arraySnapElement = arraySnap.valueAt(0);
+ verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick());
+ }
+ // Verify copy-in/out
+ {
+ final String msg = name + " copy-in/out";
+ LongSparseArray<Leaf> base = new LongSparseArray<>();
+ array.copyTo(base);
+ WatchedLongSparseArray<Leaf> copy = new WatchedLongSparseArray<>();
+ copy.copyFrom(base);
+ final int end = array.size();
+ assertTrue(msg + " size mismatch " + end + " " + copy.size(), end == copy.size());
+ for (int i = 0; i < end; i++) {
+ final long key = array.keyAt(i);
+ assertTrue(msg, array.get(i) == copy.get(i));
+ }
+ }
+ }
+
+ @Test
public void testWatchedSparseBooleanArray() {
final String name = "WatchedSparseBooleanArray";
WatchableTester tester;
@@ -733,4 +857,43 @@
}
}
}
+
+ @Test
+ public void testNestedArrays() {
+ final String name = "NestedArrays";
+ WatchableTester tester;
+
+ // Create a few leaves
+ Leaf leafA = new Leaf();
+ Leaf leafB = new Leaf();
+ Leaf leafC = new Leaf();
+ Leaf leafD = new Leaf();
+
+ // Test nested arrays.
+ WatchedLongSparseArray<Leaf> lsaA = new WatchedLongSparseArray<>();
+ lsaA.put(2, leafA);
+ WatchedLongSparseArray<Leaf> lsaB = new WatchedLongSparseArray<>();
+ lsaB.put(4, leafB);
+ WatchedLongSparseArray<Leaf> lsaC = new WatchedLongSparseArray<>();
+ lsaC.put(6, leafC);
+
+ WatchedArrayMap<String, WatchedLongSparseArray<Leaf>> array =
+ new WatchedArrayMap<>();
+ array.put("A", lsaA);
+ array.put("B", lsaB);
+
+ // Test WatchedSparseIntArray
+ tester = new WatchableTester(array, name);
+ tester.verify(0, "Initial array - no registration");
+ tester.register();
+ tester.verify(0, "Initial array - post registration");
+ leafA.tick();
+ tester.verify(1, "tick grand-leaf");
+ lsaA.put(2, leafD);
+ tester.verify(2, "replace leafA");
+ leafA.tick();
+ tester.verify(2, "tick unregistered leafA");
+ leafD.tick();
+ tester.verify(3, "tick leafD");
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 35b224a..2ae2ef7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -51,6 +51,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
+import com.android.server.pm.PackageManagerService;
import org.junit.Before;
import org.junit.Test;
@@ -259,6 +260,17 @@
}
@Test
+ public void testSnoozeSentToAndroid() throws Exception {
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 1000);
+ ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAm, times(1)).setExactAndAllowWhileIdle(
+ anyInt(), anyLong(), captor.capture());
+ assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
+ captor.getValue().getIntent().getPackage());
+ }
+
+ @Test
public void testSnooze() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, (String) null);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index aa1110c..488e629 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2288,7 +2288,7 @@
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
reset(task);
activity.reportDescendantOrientationChangeIfNeeded();
- verify(task).onConfigurationChanged(any(Configuration.class));
+ verify(task, atLeast(1)).onConfigurationChanged(any(Configuration.class));
}
@Test
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 58169bb..dc702e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -32,6 +32,7 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
@@ -121,6 +122,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Test;
@@ -955,16 +957,14 @@
IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
final int newOrientation = getRotatedOrientation(dc);
- final Task stack = new TaskBuilder(mSupervisor)
+ final Task task = new TaskBuilder(mSupervisor)
.setDisplay(dc).setCreateActivity(true).build();
- final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity();
+ final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity();
+ dc.setFocusedApp(activity);
activity.setRequestedOrientation(newOrientation);
- final int expectedOrientation = newOrientation == SCREEN_ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_PORTRAIT
- : Configuration.ORIENTATION_LANDSCAPE;
- assertEquals(expectedOrientation, dc.getConfiguration().orientation);
+ assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1);
}
@Test
@@ -972,17 +972,42 @@
final DisplayContent dc = createNewDisplay();
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
+ dc.getDisplayRotation().setUserRotation(
+ WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180);
final int newOrientation = getRotatedOrientation(dc);
- final Task stack = new TaskBuilder(mSupervisor)
+ final Task task = new TaskBuilder(mSupervisor)
.setDisplay(dc).setCreateActivity(true).build();
- final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity();
+ final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity();
+ dc.setFocusedApp(activity);
activity.setRequestedOrientation(newOrientation);
verify(dc, never()).updateDisplayOverrideConfigurationLocked(any(), eq(activity),
anyBoolean(), same(null));
- assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation());
+ assertEquals(ROTATION_180, dc.getRotation());
+ }
+
+ @Test
+ public void testFixedToUserRotationChanged() {
+ final DisplayContent dc = createNewDisplay();
+ dc.getDisplayRotation().setFixedToUserRotation(
+ IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
+ dc.getDisplayRotation().setUserRotation(
+ WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0);
+ final int newOrientation = getRotatedOrientation(dc);
+
+ final Task task = new TaskBuilder(mSupervisor)
+ .setDisplay(dc).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity();
+ dc.setFocusedApp(activity);
+
+ activity.setRequestedOrientation(newOrientation);
+
+ dc.getDisplayRotation().setFixedToUserRotation(
+ IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
+
+ assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index b1ea4a5..5a0466a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -130,7 +130,7 @@
// insets state with the global one.
final InsetsState insetsState =
win.getDisplayContent().getInsetsStateController().getRawInsetsState();
- win.mAboveInsetsState = insetsState;
+ win.mAboveInsetsState.set(insetsState);
}
public void setRotation(int rotation, boolean includingWindows) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 2163661..47cf53b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -18,6 +18,7 @@
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.RoundedCorners.NO_ROUNDED_CORNERS;
import static android.view.Surface.ROTATION_0;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -37,6 +38,7 @@
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
+import static com.android.server.wm.utils.WmDisplayCutout.NO_CUTOUT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -300,9 +302,9 @@
displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
- mImeWindow.mAboveInsetsState = state;
+ mImeWindow.mAboveInsetsState.set(state);
mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
- state, displayInfo, null /* displayCutout */, null /* roundedCorners*/);
+ state, displayInfo, NO_CUTOUT, NO_ROUNDED_CORNERS);
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 20775e8..683ed88 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -117,7 +117,7 @@
static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
boolean withDisplayCutout, boolean isLongEdgeCutout) {
final DisplayInfo info = new DisplayInfo();
- WmDisplayCutout cutout = null;
+ WmDisplayCutout cutout = WmDisplayCutout.NO_CUTOUT;
final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index ee293fc..be03603 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -32,13 +32,14 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -208,7 +209,7 @@
app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
// Make sure app got notified.
- verify(app, atLeast(1)).notifyInsetsChanged();
+ verify(app, atLeastOnce()).notifyInsetsChanged();
// app will get visible IME insets while below IME.
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
@@ -336,6 +337,92 @@
assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
}
+ @Test
+ public void testUpdateAboveInsetsState_provideInsets() {
+ final WindowState app = createTestWindow("app");
+ final WindowState statusBar = createTestWindow("statusBar");
+ final WindowState navBar = createTestWindow("navBar");
+
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+
+ assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+
+ getController().updateAboveInsetsState(statusBar, true /* notifyInsetsChange */);
+
+ assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+ }
+
+ @Test
+ public void testUpdateAboveInsetsState_receiveInsets() {
+ final WindowState app = createTestWindow("app");
+ final WindowState statusBar = createTestWindow("statusBar");
+ final WindowState navBar = createTestWindow("navBar");
+
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+ getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+
+ assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
+
+ getController().updateAboveInsetsState(app, true /* notifyInsetsChange */);
+
+ assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
+
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+ }
+
+ @Test
+ public void testUpdateAboveInsetsState_zOrderChanged() {
+ final WindowState ime = createTestWindow("ime");
+ final WindowState app = createTestWindow("app");
+ final WindowState statusBar = createTestWindow("statusBar");
+ final WindowState navBar = createTestWindow("navBar");
+
+ getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+ getController().getSourceProvider(ITYPE_IME).setClientVisible(true);
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+ getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+ getController().updateAboveInsetsState(ime, false /* notifyInsetsChange */);
+ getController().updateAboveInsetsState(statusBar, false /* notifyInsetsChange */);
+ getController().updateAboveInsetsState(navBar, false /* notifyInsetsChange */);
+
+ // ime is below others.
+ assertNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
+
+ ime.getParent().positionChildAt(POSITION_TOP, ime, true /* includingParents */);
+ getController().updateAboveInsetsState(ime, true /* notifyInsetsChange */);
+
+ // ime is above others.
+ assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNotNull(statusBar.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNotNull(navBar.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNull(ime.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
+
+ verify(ime, atLeastOnce()).notifyInsetsChanged();
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+ verify(statusBar, atLeastOnce()).notifyInsetsChanged();
+ verify(navBar, atLeastOnce()).notifyInsetsChanged();
+ }
+
+ private WindowState createTestWindow(String name) {
+ final WindowState win = createWindow(null, TYPE_APPLICATION, name);
+ win.setHasSurface(true);
+ spyOn(win);
+ return win;
+ }
+
private InsetsStateController getController() {
return mDisplayContent.getInsetsStateController();
}
diff --git a/services/texttospeech/Android.bp b/services/texttospeech/Android.bp
index bacc932..391ab89 100644
--- a/services/texttospeech/Android.bp
+++ b/services/texttospeech/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.texttospeech-sources",
srcs: ["java/**/*.java"],
@@ -10,4 +19,4 @@
defaults: ["platform_service_defaults"],
srcs: [":services.texttospeech-sources"],
libs: ["services.core"],
-}
\ No newline at end of file
+}
diff --git a/services/translation/Android.bp b/services/translation/Android.bp
index 804a617..f257f1b 100644
--- a/services/translation/Android.bp
+++ b/services/translation/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.translation-sources",
srcs: ["java/**/*.java"],
@@ -10,4 +19,4 @@
defaults: ["platform_service_defaults"],
srcs: [":services.translation-sources"],
libs: ["services.core"],
-}
\ No newline at end of file
+}
diff --git a/telecomm/java/android/telecom/Connection.aidl b/telecomm/java/android/telecom/Connection.aidl
new file mode 100644
index 0000000..5b40036
--- /dev/null
+++ b/telecomm/java/android/telecom/Connection.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+parcelable Connection.CallFilteringCompletionInfo;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 942a54e..7c6253ce 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -29,6 +29,7 @@
import android.app.Notification;
import android.bluetooth.BluetoothDevice;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -38,7 +39,9 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.telephony.ims.ImsStreamMediaProfile;
@@ -3379,6 +3382,121 @@
public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
/**
+ * Information provided to a {@link Connection} upon completion of call filtering in Telecom.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class CallFilteringCompletionInfo implements Parcelable {
+ private final boolean mIsBlocked;
+ private final boolean mIsInContacts;
+ private final CallScreeningService.CallResponse mCallResponse;
+ private final ComponentName mCallScreeningComponent;
+
+ /**
+ * Constructor for {@link CallFilteringCompletionInfo}
+ *
+ * @param isBlocked Whether any part of the call filtering process indicated that this call
+ * should be blocked.
+ * @param isInContacts Whether the caller is in the user's contacts.
+ * @param callResponse The instance of {@link CallScreeningService.CallResponse} provided
+ * by the {@link CallScreeningService} that processed this call, or
+ * {@code null} if no call screening service ran.
+ * @param callScreeningComponent The component of the {@link CallScreeningService}
+ * that processed this call, or {@link null} if no
+ * call screening service ran.
+ */
+ public CallFilteringCompletionInfo(boolean isBlocked, boolean isInContacts,
+ @Nullable CallScreeningService.CallResponse callResponse,
+ @Nullable ComponentName callScreeningComponent) {
+ mIsBlocked = isBlocked;
+ mIsInContacts = isInContacts;
+ mCallResponse = callResponse;
+ mCallScreeningComponent = callScreeningComponent;
+ }
+
+ /** @hide */
+ protected CallFilteringCompletionInfo(Parcel in) {
+ mIsBlocked = in.readByte() != 0;
+ mIsInContacts = in.readByte() != 0;
+ CallScreeningService.ParcelableCallResponse response
+ = in.readParcelable(CallScreeningService.class.getClassLoader());
+ mCallResponse = response == null ? null : response.toCallResponse();
+ mCallScreeningComponent = in.readParcelable(ComponentName.class.getClassLoader());
+ }
+
+ @NonNull
+ public static final Creator<CallFilteringCompletionInfo> CREATOR =
+ new Creator<CallFilteringCompletionInfo>() {
+ @Override
+ public CallFilteringCompletionInfo createFromParcel(Parcel in) {
+ return new CallFilteringCompletionInfo(in);
+ }
+
+ @Override
+ public CallFilteringCompletionInfo[] newArray(int size) {
+ return new CallFilteringCompletionInfo[size];
+ }
+ };
+
+ /**
+ * @return Whether any part of the call filtering process indicated that this call should be
+ * blocked.
+ */
+ public boolean isBlocked() {
+ return mIsBlocked;
+ }
+
+ /**
+ * @return Whether the caller is in the user's contacts.
+ */
+ public boolean isInContacts() {
+ return mIsInContacts;
+ }
+
+ /**
+ * @return The instance of {@link CallScreeningService.CallResponse} provided
+ * by the {@link CallScreeningService} that processed this
+ * call, or {@code null} if no call screening service ran.
+ */
+ public @Nullable CallScreeningService.CallResponse getCallResponse() {
+ return mCallResponse;
+ }
+
+ /**
+ * @return The component of the {@link CallScreeningService}
+ * that processed this call, or {@code null} if no call screening service ran.
+ */
+ public @Nullable ComponentName getCallScreeningComponent() {
+ return mCallScreeningComponent;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "CallFilteringCompletionInfo{" +
+ "mIsBlocked=" + mIsBlocked +
+ ", mIsInContacts=" + mIsInContacts +
+ ", mCallResponse=" + mCallResponse +
+ ", mCallScreeningPackageName='" + mCallScreeningComponent + '\'' +
+ '}';
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mIsBlocked ? 1 : 0));
+ dest.writeByte((byte) (mIsInContacts ? 1 : 0));
+ dest.writeParcelable(mCallResponse == null ? null : mCallResponse.toParcelable(), 0);
+ dest.writeParcelable(mCallScreeningComponent, 0);
+ }
+ }
+
+ /**
* Indicates that call filtering in Telecom is complete
*
* This method is called for a connection created via
@@ -3386,24 +3504,13 @@
* Telecom, including checking the blocked number db, per-contact settings, and custom call
* filtering apps.
*
- * @param isBlocked {@code true} if the call was blocked, {@code false} otherwise. If this is
- * {@code true}, {@link #onDisconnect()} will be called soon after
- * this is called.
- * @param isInContacts Indicates whether the caller is in the user's contacts list.
- * @param callScreeningResponse The response that was returned from the
- * {@link CallScreeningService} that handled this call. If no
- * response was received from a call screening service,
- * this will be {@code null}.
- * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
- * system dialer. If {@code callScreeningResponse} is
- * {@code null}, this will be {@code false}.
+ * @param callFilteringCompletionInfo Info provided by Telecom on the results of call filtering.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_CONTACTS)
- public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
- @Nullable CallScreeningService.CallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer) { }
+ public void onCallFilteringCompleted(
+ @NonNull CallFilteringCompletionInfo callFilteringCompletionInfo) { }
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 966ece3..c189b19 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -758,19 +758,15 @@
}
@Override
- public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
- CallScreeningService.ParcelableCallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer,
+ public void onCallFilteringCompleted(String callId,
+ Connection.CallFilteringCompletionInfo completionInfo,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED);
try {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
- args.arg2 = isBlocked;
- args.arg3 = isInContacts;
- args.arg4 = callScreeningResponse;
- args.arg5 = isResponseFromSystemDialer;
- args.arg6 = Log.createSubsession();
+ args.arg2 = completionInfo;
+ args.arg3 = Log.createSubsession();
mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget();
} finally {
Log.endSession();
@@ -1441,16 +1437,12 @@
case MSG_ON_CALL_FILTERING_COMPLETED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- Log.continueSession((Session) args.arg6,
+ Log.continueSession((Session) args.arg3,
SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED);
String callId = (String) args.arg1;
- boolean isBlocked = (boolean) args.arg2;
- boolean isInContacts = (boolean) args.arg3;
- CallScreeningService.ParcelableCallResponse callScreeningResponse =
- (CallScreeningService.ParcelableCallResponse) args.arg4;
- boolean isResponseFromSystemDialer = (boolean) args.arg5;
- onCallFilteringCompleted(callId, isBlocked, isInContacts,
- callScreeningResponse, isResponseFromSystemDialer);
+ Connection.CallFilteringCompletionInfo completionInfo =
+ (Connection.CallFilteringCompletionInfo) args.arg2;
+ onCallFilteringCompleted(callId, completionInfo);
} finally {
args.recycle();
Log.endSession();
@@ -2466,16 +2458,12 @@
}
}
- private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
- CallScreeningService.ParcelableCallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer) {
- Log.i(this, "onCallFilteringCompleted(%s, %b, %b, %s, %b)", callId,
- isBlocked, isInContacts, callScreeningResponse, isResponseFromSystemDialer);
+ private void onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo
+ callFilteringCompletionInfo) {
+ Log.i(this, "onCallFilteringCompleted(%s, %s)", callId, callFilteringCompletionInfo);
Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
if (connection != null) {
- connection.onCallFilteringCompleted(isBlocked, isInContacts,
- callScreeningResponse == null ? null : callScreeningResponse.toCallResponse(),
- isResponseFromSystemDialer);
+ connection.onCallFilteringCompleted(callFilteringCompletionInfo);
}
}
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 6c6097a..7a6fddb 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -1202,27 +1202,18 @@
/**
* Notifies this {@link RemoteConnection} that call filtering has completed, as well as
* the results of a contacts lookup for the remote party.
- * @param isBlocked Whether call filtering indicates that the call should be blocked
- * @param isInContacts Whether the remote party is in the user's contacts
- * @param callScreeningResponse The response that was returned from the
- * {@link CallScreeningService} that handled this call. If no
- * response was received from a call screening service,
- * this will be {@code null}.
- * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
- * system dialer. If {@code callScreeningResponse} is
- * {@code null}, this will be {@code false}.
+ *
+ * @param completionInfo Info provided by Telecom on the results of call filtering.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_CONTACTS)
- public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
- @Nullable CallScreeningService.CallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer) {
+ public void onCallFilteringCompleted(
+ @NonNull Connection.CallFilteringCompletionInfo completionInfo) {
Log.startSession("RC.oCFC", getActiveOwnerInfo());
try {
if (mConnected) {
- mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts,
- callScreeningResponse.toParcelable(), isResponseFromSystemDialer,
+ mConnectionService.onCallFilteringCompleted(mConnectionId, completionInfo,
null /*Session.Info*/);
}
} catch (RemoteException ignored) {
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 7599e18..d72f8aa 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -20,7 +20,7 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
-import android.telecom.CallScreeningService;
+import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.Logging.Session;
import android.telecom.PhoneAccountHandle;
@@ -119,9 +119,9 @@
void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
- void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
- in CallScreeningService.ParcelableCallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer, in Session.Info sessionInfo);
+ void onCallFilteringCompleted(String callId,
+ in Connection.CallFilteringCompletionInfo completionInfo,
+ in Session.Info sessionInfo);
void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index c6757fb..4ae11b8 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -111,6 +111,7 @@
public @interface NetworkType {
}
+ // TODO(b/180542000): remove and replace references with @ApnSetting.ApnType
@IntDef(flag = true, prefix = {"TYPE_"}, value = {
ApnSetting.TYPE_DEFAULT,
ApnSetting.TYPE_MMS,
@@ -124,6 +125,7 @@
ApnSetting.TYPE_EMERGENCY,
ApnSetting.TYPE_MCX,
ApnSetting.TYPE_XCAP,
+ // ApnSetting.TYPE_ENTERPRISE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApnType {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8c68d85..3d43d03 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5321,7 +5321,7 @@
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
new String[0]);
sDefaults.putStringArray(KEY_APN_PRIORITY_STRING_ARRAY, new String[] {
- "default:0", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2",
+ "default:0", "enterprise:1", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2",
"ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3"
});
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 99a77ae5..c8ed82c 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -917,6 +917,10 @@
public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB;
/** Data call fail due to the slice not being allowed for the data call. */
public static final int SLICE_REJECTED = 0x8CC;
+ /** No matching rule available for the request, and match-all rule is not allowed for it. */
+ public static final int MATCH_ALL_RULE_NOT_ALLOWED = 0x8CD;
+ /** If connection failed for all matching URSP rules. */
+ public static final int ALL_MATCHING_RULES_FAILED = 0x8CE;
//IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2).
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 6c013df..4926687 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2649,6 +2649,37 @@
*/
public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
Bundle configOverrides, PendingIntent sentIntent) {
+ sendMultimediaMessage(context, contentUri, locationUrl, configOverrides, sentIntent,
+ 0L /* messageId */);
+ }
+
+ /**
+ * Send an MMS message
+ *
+ * Same as {@link #sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
+ * Bundle configOverrides, PendingIntent sentIntent)}, but adds an optional messageId.
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the MMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+ * conditions where this operation may fail.
+ * </p>
+ *
+ * @param context application context
+ * @param contentUri the content Uri from which the message pdu will be read
+ * @param locationUrl the optional location url where message should be sent to
+ * @param configOverrides the carrier-specific messaging configuration values to override for
+ * sending the message.
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed
+ * @param messageId an id that uniquely identifies the message requested to be sent.
+ * Used for logging and diagnostics purposes. The id may be 0.
+ * @throws IllegalArgumentException if contentUri is empty
+ */
+ public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri,
+ @Nullable String locationUrl, @Nullable Bundle configOverrides,
+ @Nullable PendingIntent sentIntent, long messageId) {
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
}
@@ -2658,7 +2689,7 @@
@Override
public void onSuccess(int subId) {
m.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides,
- sentIntent, 0L /* messageId */);
+ sentIntent, messageId);
}
@Override
@@ -2692,6 +2723,39 @@
*/
public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
Bundle configOverrides, PendingIntent downloadedIntent) {
+ downloadMultimediaMessage(context, locationUrl, contentUri, configOverrides,
+ downloadedIntent, 0L /* messageId */);
+ }
+
+ /**
+ * Download an MMS message from carrier by a given location URL
+ *
+ * Same as {@link #downloadMultimediaMessage(Context context, String locationUrl,
+ * Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)},
+ * but adds an optional messageId.
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail downloading the MMS message because no
+ * suitable default subscription could be found. In this case, if {@code downloadedIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+ * conditions where this operation may fail.
+ * </p>
+ *
+ * @param context application context
+ * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
+ * from the MMS WAP push notification
+ * @param contentUri the content uri to which the downloaded pdu will be written
+ * @param configOverrides the carrier-specific messaging configuration values to override for
+ * downloading the message.
+ * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is downloaded, or the download is failed
+ * @param messageId an id that uniquely identifies the message requested to be downloaded.
+ * Used for logging and diagnostics purposes. The id may be 0.
+ * @throws IllegalArgumentException if locationUrl or contentUri is empty
+ */
+ public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl,
+ @NonNull Uri contentUri, @Nullable Bundle configOverrides,
+ @Nullable PendingIntent downloadedIntent, long messageId) {
if (TextUtils.isEmpty(locationUrl)) {
throw new IllegalArgumentException("Empty MMS location URL");
}
@@ -2704,7 +2768,7 @@
@Override
public void onSuccess(int subId) {
m.downloadMultimediaMessage(subId, locationUrl, contentUri, configOverrides,
- downloadedIntent, 0L /* messageId */);
+ downloadedIntent, messageId);
}
@Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d545c65..f64f428 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11293,21 +11293,16 @@
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
- * Additionally, depending on the level of location permissions the caller holds (i.e. no
- * location permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, or
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}), location-sensitive fields will
- * be cleared from the return value.
- *
- * <p>Note also that if the caller holds any sort of location permission, a usage event for the
- * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION} or
- * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION}
- * will be logged against the caller when calling this method.
+ * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
*
* May return {@code null} when the subscription is inactive or when there was an error
* communicating with the phone process.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ })
public @Nullable ServiceState getServiceState() {
return getServiceStateForSubscriber(getSubId());
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index d58fa912..b503733 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -28,8 +28,6 @@
import android.os.Parcelable;
import android.provider.Telephony;
import android.provider.Telephony.Carriers;
-import android.telephony.Annotation;
-import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.NetworkType;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
@@ -116,6 +114,31 @@
public static final int TYPE_MCX = ApnTypes.MCX;
/** APN type for XCAP. */
public static final int TYPE_XCAP = ApnTypes.XCAP;
+ /**
+ * APN type for ENTERPRISE.
+ * @hide
+ */
+ public static final int TYPE_ENTERPRISE = TYPE_XCAP << 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = {"TYPE_"}, value = {
+ TYPE_DEFAULT,
+ TYPE_MMS,
+ TYPE_SUPL,
+ TYPE_DUN,
+ TYPE_HIPRI,
+ TYPE_FOTA,
+ TYPE_IMS,
+ TYPE_CBS,
+ TYPE_IA,
+ TYPE_EMERGENCY,
+ TYPE_MCX,
+ TYPE_XCAP,
+ TYPE_ENTERPRISE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApnType {
+ }
// Possible values for authentication types.
/** No authentication type. */
@@ -151,6 +174,7 @@
TYPE_MMS_STRING,
TYPE_SUPL_STRING,
TYPE_XCAP_STRING,
+ TYPE_ENTERPRISE_STRING,
}, prefix = "TYPE_", suffix = "_STRING")
@Retention(RetentionPolicy.SOURCE)
public @interface ApnTypeString {}
@@ -291,6 +315,12 @@
@SystemApi
public static final String TYPE_XCAP_STRING = "xcap";
+ /**
+ * APN type for ENTERPRISE traffic.
+ * @hide
+ */
+ public static final String TYPE_ENTERPRISE_STRING = "enterprise";
+
/** @hide */
@IntDef(prefix = { "AUTH_TYPE_" }, value = {
@@ -370,6 +400,7 @@
APN_TYPE_STRING_MAP.put(TYPE_EMERGENCY_STRING, TYPE_EMERGENCY);
APN_TYPE_STRING_MAP.put(TYPE_MCX_STRING, TYPE_MCX);
APN_TYPE_STRING_MAP.put(TYPE_XCAP_STRING, TYPE_XCAP);
+ APN_TYPE_STRING_MAP.put(TYPE_ENTERPRISE_STRING, TYPE_ENTERPRISE);
APN_TYPE_INT_MAP = new ArrayMap<>();
APN_TYPE_INT_MAP.put(TYPE_DEFAULT, TYPE_DEFAULT_STRING);
@@ -384,6 +415,7 @@
APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, TYPE_EMERGENCY_STRING);
APN_TYPE_INT_MAP.put(TYPE_MCX, TYPE_MCX_STRING);
APN_TYPE_INT_MAP.put(TYPE_XCAP, TYPE_XCAP_STRING);
+ APN_TYPE_INT_MAP.put(TYPE_ENTERPRISE, TYPE_ENTERPRISE_STRING);
PROTOCOL_STRING_MAP = new ArrayMap<>();
PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
@@ -1490,7 +1522,7 @@
* @hide
*/
@SystemApi
- public static @NonNull @ApnTypeString String getApnTypeString(@Annotation.ApnType int apnType) {
+ public static @NonNull @ApnTypeString String getApnTypeString(@ApnType int apnType) {
if (apnType == TYPE_ALL) {
return "*";
}
@@ -1503,7 +1535,7 @@
* when provided with an invalid int for compatibility purposes.
* @hide
*/
- public static @NonNull String getApnTypeStringInternal(@Annotation.ApnType int apnType) {
+ public static @NonNull String getApnTypeStringInternal(@ApnType int apnType) {
String result = getApnTypeString(apnType);
return TextUtils.isEmpty(result) ? "Unknown" : result;
}
@@ -1517,7 +1549,7 @@
* @hide
*/
@SystemApi
- public static @Annotation.ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) {
+ public static @ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) {
return APN_TYPE_STRING_MAP.getOrDefault(apnType.toLowerCase(), 0);
}
@@ -2162,7 +2194,7 @@
public ApnSetting build() {
if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI
| TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX
- | TYPE_XCAP)) == 0
+ | TYPE_XCAP | TYPE_ENTERPRISE)) == 0
|| TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) {
return null;
}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index bd4bf07..a764229 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -138,6 +138,7 @@
private final Qos mDefaultQos;
private final List<QosBearerSession> mQosBearerSessions;
private final SliceInfo mSliceInfo;
+ private final List<TrafficDescriptor> mTrafficDescriptors;
/**
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -189,6 +190,7 @@
mDefaultQos = null;
mQosBearerSessions = new ArrayList<>();
mSliceInfo = null;
+ mTrafficDescriptors = new ArrayList<>();
}
private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -198,7 +200,7 @@
@Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
@HandoverFailureMode int handoverFailureMode, int pduSessionId,
@Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions,
- @Nullable SliceInfo sliceInfo) {
+ @Nullable SliceInfo sliceInfo, @Nullable List<TrafficDescriptor> trafficDescriptors) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
@@ -219,8 +221,11 @@
mHandoverFailureMode = handoverFailureMode;
mPduSessionId = pduSessionId;
mDefaultQos = defaultQos;
- mQosBearerSessions = qosBearerSessions;
+ mQosBearerSessions = (qosBearerSessions == null)
+ ? new ArrayList<>() : new ArrayList<>(qosBearerSessions);
mSliceInfo = sliceInfo;
+ mTrafficDescriptors = (trafficDescriptors == null)
+ ? new ArrayList<>() : new ArrayList<>(trafficDescriptors);
}
/** @hide */
@@ -249,6 +254,8 @@
mQosBearerSessions = new ArrayList<>();
source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader());
mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader());
+ mTrafficDescriptors = new ArrayList<>();
+ source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader());
}
/**
@@ -381,7 +388,6 @@
*
* @hide
*/
-
@Nullable
public Qos getDefaultQos() {
return mDefaultQos;
@@ -406,6 +412,14 @@
return mSliceInfo;
}
+ /**
+ * @return The traffic descriptors related to this data connection.
+ */
+ @NonNull
+ public List<TrafficDescriptor> getTrafficDescriptors() {
+ return mTrafficDescriptors;
+ }
+
@NonNull
@Override
public String toString() {
@@ -429,6 +443,7 @@
.append(" defaultQos=").append(mDefaultQos)
.append(" qosBearerSessions=").append(mQosBearerSessions)
.append(" sliceInfo=").append(mSliceInfo)
+ .append(" trafficDescriptors=").append(mTrafficDescriptors)
.append("}");
return sb.toString();
}
@@ -443,15 +458,22 @@
DataCallResponse other = (DataCallResponse) o;
- final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) ?
- mDefaultQos == other.mDefaultQos :
- mDefaultQos.equals(other.mDefaultQos);
+ final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null)
+ ? mDefaultQos == other.mDefaultQos
+ : mDefaultQos.equals(other.mDefaultQos);
- final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ?
- mQosBearerSessions == other.mQosBearerSessions :
- mQosBearerSessions.size() == other.mQosBearerSessions.size()
+ final boolean isQosBearerSessionsSame =
+ (mQosBearerSessions == null || other.mQosBearerSessions == null)
+ ? mQosBearerSessions == other.mQosBearerSessions
+ : mQosBearerSessions.size() == other.mQosBearerSessions.size()
&& mQosBearerSessions.containsAll(other.mQosBearerSessions);
+ final boolean isTrafficDescriptorsSame =
+ (mTrafficDescriptors == null || other.mTrafficDescriptors == null)
+ ? mTrafficDescriptors == other.mTrafficDescriptors
+ : mTrafficDescriptors.size() == other.mTrafficDescriptors.size()
+ && mTrafficDescriptors.containsAll(other.mTrafficDescriptors);
+
return mCause == other.mCause
&& mSuggestedRetryTime == other.mSuggestedRetryTime
&& mId == other.mId
@@ -473,7 +495,8 @@
&& mPduSessionId == other.mPduSessionId
&& isQosSame
&& isQosBearerSessionsSame
- && Objects.equals(mSliceInfo, other.mSliceInfo);
+ && Objects.equals(mSliceInfo, other.mSliceInfo)
+ && isTrafficDescriptorsSame;
}
@Override
@@ -481,7 +504,7 @@
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
- mQosBearerSessions, mSliceInfo);
+ mQosBearerSessions, mSliceInfo, mTrafficDescriptors);
}
@Override
@@ -517,6 +540,7 @@
}
dest.writeList(mQosBearerSessions);
dest.writeParcelable(mSliceInfo, flags);
+ dest.writeList(mTrafficDescriptors);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -602,6 +626,8 @@
private SliceInfo mSliceInfo;
+ private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
+
/**
* Default constructor for Builder.
*/
@@ -841,6 +867,24 @@
}
/**
+ * The traffic descriptors for this data connection, as defined in 3GPP TS 24.526
+ * Section 5.2. They are used for URSP traffic matching as described in 3GPP TS 24.526
+ * Section 4.2.2. They includes an optional DNN, which, if present, must be used for traffic
+ * matching; it does not specify the end point to be used for the data call. The end point
+ * is specified by {@link DataProfile}, which must be used as the end point if one is not
+ * specified through URSP rules.
+ *
+ * @param trafficDescriptors the traffic descriptors for the data call.
+ *
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setTrafficDescriptors(
+ @NonNull List<TrafficDescriptor> trafficDescriptors) {
+ mTrafficDescriptors = trafficDescriptors;
+ return this;
+ }
+
+ /**
* Build the DataCallResponse.
*
* @return the DataCallResponse object.
@@ -849,7 +893,7 @@
return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
- mDefaultQos, mQosBearerSessions, mSliceInfo);
+ mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors);
}
}
}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 484c318..f5f29c6 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -200,6 +200,17 @@
* handover is occurring from EPDG to 5G. If the slice passed is rejected, then
* {@link DataCallResponse#getCause()} is
* {@link android.telephony.DataFailCause#SLICE_REJECTED}.
+ * @param trafficDescriptor {@link TrafficDescriptor} for which data connection needs to be
+ * established. It is used for URSP traffic matching as described in 3GPP TS 24.526
+ * Section 4.2.2. It includes an optional DNN which, if present, must be used for
+ * traffic matching; it does not specify the end point to be used for the data call.
+ * @param matchAllRuleAllowed Indicates if using default match-all URSP rule for this
+ * request is allowed. If false, this request must not use the match-all URSP rule
+ * and if a non-match-all rule is not found (or if URSP rules are not available) then
+ * {@link DataCallResponse#getCause()} is
+ * {@link android.telephony.DataFailCause#MATCH_ALL_RULE_NOT_ALLOWED}. This is needed
+ * as some requests need to have a hard failure if the intention cannot be met,
+ * for example, a zero-rating slice.
* @param callback The result callback for this request.
*/
public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile,
@@ -207,6 +218,7 @@
@SetupDataReason int reason,
@Nullable LinkProperties linkProperties,
@IntRange(from = 0, to = 15) int pduSessionId, @Nullable SliceInfo sliceInfo,
+ @Nullable TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
@NonNull DataServiceCallback callback) {
/* Call the old version since the new version isn't supported */
setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason,
@@ -403,10 +415,13 @@
public final LinkProperties linkProperties;
public final int pduSessionId;
public final SliceInfo sliceInfo;
+ public final TrafficDescriptor trafficDescriptor;
+ public final boolean matchAllRuleAllowed;
public final IDataServiceCallback callback;
SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- int pduSessionId, SliceInfo sliceInfo, IDataServiceCallback callback) {
+ boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+ SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed, IDataServiceCallback callback) {
this.accessNetworkType = accessNetworkType;
this.dataProfile = dataProfile;
this.isRoaming = isRoaming;
@@ -415,6 +430,8 @@
this.reason = reason;
this.pduSessionId = pduSessionId;
this.sliceInfo = sliceInfo;
+ this.trafficDescriptor = trafficDescriptor;
+ this.matchAllRuleAllowed = matchAllRuleAllowed;
this.callback = callback;
}
}
@@ -525,7 +542,8 @@
setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId,
- setupDataCallRequest.sliceInfo,
+ setupDataCallRequest.sliceInfo, setupDataCallRequest.trafficDescriptor,
+ setupDataCallRequest.matchAllRuleAllowed,
(setupDataCallRequest.callback != null)
? new DataServiceCallback(setupDataCallRequest.callback)
: null);
@@ -690,11 +708,12 @@
public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile,
boolean isRoaming, boolean allowRoaming, int reason,
LinkProperties linkProperties, int pduSessionId, SliceInfo sliceInfo,
+ TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
IDataServiceCallback callback) {
mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0,
new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
- callback))
+ trafficDescriptor, matchAllRuleAllowed, callback))
.sendToTarget();
}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index e0b9a1a..81f5fd3 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -20,6 +20,7 @@
import android.telephony.data.DataProfile;
import android.telephony.data.IDataServiceCallback;
import android.telephony.data.SliceInfo;
+import android.telephony.data.TrafficDescriptor;
/**
* {@hide}
@@ -30,7 +31,9 @@
void removeDataServiceProvider(int slotId);
void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
boolean allowRoaming, int reason, in LinkProperties linkProperties,
- int pduSessionId, in SliceInfo sliceInfo, IDataServiceCallback callback);
+ int pduSessionId, in SliceInfo sliceInfo,
+ in TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
+ IDataServiceCallback callback);
void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback);
void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming,
IDataServiceCallback callback);
diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.aidl b/telephony/java/android/telephony/data/TrafficDescriptor.aidl
new file mode 100644
index 0000000..a9c7604
--- /dev/null
+++ b/telephony/java/android/telephony/data/TrafficDescriptor.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable TrafficDescriptor;
diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java
new file mode 100644
index 0000000..480379d
--- /dev/null
+++ b/telephony/java/android/telephony/data/TrafficDescriptor.java
@@ -0,0 +1,111 @@
+/*
+ * 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.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for URSP traffic
+ * matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an optional DNN, which,
+ * if present, must be used for traffic matching; it does not specify the end point to be used for
+ * the data call.
+ * @hide
+ */
+@SystemApi
+public final class TrafficDescriptor implements Parcelable {
+ private final String mDnn;
+ private final String mOsAppId;
+
+ private TrafficDescriptor(@NonNull Parcel in) {
+ mDnn = in.readString();
+ mOsAppId = in.readString();
+ }
+
+ /**
+ * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2
+ * @param dnn optional DNN, which must be used for traffic matching, if present
+ * @param osAppId OsId + osAppId of the traffic descriptor
+ */
+ public TrafficDescriptor(@Nullable String dnn, @Nullable String osAppId) {
+ mDnn = dnn;
+ mOsAppId = osAppId;
+ }
+
+ /**
+ * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003.
+ * @return the DNN of this traffic descriptor.
+ */
+ public @Nullable String getDnn() {
+ return mDnn;
+ }
+
+ /**
+ * OsAppId represents the OsId + OsAppId as defined in 3GPP TS 24.526 Section 5.2.
+ * @return the OS App ID of this traffic descriptor.
+ */
+ public @Nullable String getOsAppId() {
+ return mOsAppId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull @Override
+ public String toString() {
+ return "TrafficDescriptor={mDnn=" + mDnn + ", mOsAppId=" + mOsAppId + "}";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mDnn);
+ dest.writeString(mOsAppId);
+ }
+
+ public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR =
+ new Parcelable.Creator<TrafficDescriptor>() {
+ @Override
+ public @NonNull TrafficDescriptor createFromParcel(@NonNull Parcel source) {
+ return new TrafficDescriptor(source);
+ }
+
+ @Override
+ public @NonNull TrafficDescriptor[] newArray(int size) {
+ return new TrafficDescriptor[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TrafficDescriptor that = (TrafficDescriptor) o;
+ return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDnn, mOsAppId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index a9dae89..9bb4db8 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -282,6 +282,15 @@
public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL";
/**
+ * Boolean extra indicating whether the call is a business call.
+ *
+ * This extra will be set to {@code true} if and only if the SIP INVITE headers contain the
+ * "Organization" header.
+ */
+ public static final String EXTRA_IS_BUSINESS_CALL =
+ "android.telephony.ims.extra.IS_BUSINESS_CALL";
+
+ /**
* Values for EXTRA_OIR / EXTRA_CNAP
*/
/**
@@ -791,7 +800,9 @@
+ ", emergencyCallTesting=" + mEmergencyCallTesting
+ ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency
+ ", mRestrictCause=" + mRestrictCause
- + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus + " }";
+ + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus
+ + ", mAcceptedRtpHeaderExtensions= " + mAcceptedRtpHeaderExtensionTypes
+ + " }";
}
@Override
diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtension.java b/telephony/java/android/telephony/ims/RtpHeaderExtension.java
index f9ab701..8815cef 100644
--- a/telephony/java/android/telephony/ims/RtpHeaderExtension.java
+++ b/telephony/java/android/telephony/ims/RtpHeaderExtension.java
@@ -134,4 +134,19 @@
result = 31 * result + Arrays.hashCode(mExtensionData);
return result;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("RtpHeaderExtension{mLocalIdentifier=");
+ sb.append(mLocalIdentifier);
+ sb.append(", mData=");
+ for (byte b : mExtensionData) {
+ sb.append(Integer.toBinaryString(b));
+ sb.append("b_");
+ }
+ sb.append("}");
+
+ return sb.toString();
+ }
}
diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java
index e1d39c2..af4e2347 100644
--- a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java
+++ b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java
@@ -133,4 +133,16 @@
public int hashCode() {
return Objects.hash(mLocalIdentifier, mUri);
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("RtpHeaderExtensionType{mLocalIdentifier=");
+ sb.append(mLocalIdentifier);
+ sb.append(", mUri=");
+ sb.append(mUri);
+ sb.append("}");
+
+ return sb.toString();
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 1d04953..0048d53 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2055,6 +2055,11 @@
int setImsProvisioningString(int subId, int key, String value);
/**
+ * Start emergency callback mode for testing.
+ */
+ void startEmergencyCallbackMode();
+
+ /**
* Update Emergency Number List for Test Mode.
*/
void updateEmergencyNumberListTestMode(int action, in EmergencyNumber num);
@@ -2340,6 +2345,11 @@
void sendDeviceToDeviceMessage(int message, int value);
/**
+ * Sets the specified transport active; only for use through shell.
+ */
+ void setActiveDeviceToDeviceTransport(String transport);
+
+ /**
* Gets the config of RCS VoLTE single registration enabled for the carrier/subscription.
*/
boolean getCarrierSingleRegistrationEnabled(int subId);
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 151187c..3a99f0e0 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -126,6 +126,7 @@
* connections.<br/>
* APN_TYPE_ALL is a special type to indicate that this APN entry can
* service all data connections.
+ * TODO: remove these and use the reference to ApnSetting.TYPE_XXX_STRING instead
*/
public static final String APN_TYPE_ALL = ApnSetting.TYPE_ALL_STRING;
/** APN type for default data traffic */
@@ -153,20 +154,8 @@
public static final String APN_TYPE_MCX = ApnSetting.TYPE_MCX_STRING;
/** APN type for XCAP */
public static final String APN_TYPE_XCAP = ApnSetting.TYPE_XCAP_STRING;
- /** Array of all APN types */
- public static final String[] APN_TYPES = {APN_TYPE_DEFAULT,
- APN_TYPE_MMS,
- APN_TYPE_SUPL,
- APN_TYPE_DUN,
- APN_TYPE_HIPRI,
- APN_TYPE_FOTA,
- APN_TYPE_IMS,
- APN_TYPE_CBS,
- APN_TYPE_IA,
- APN_TYPE_EMERGENCY,
- APN_TYPE_MCX,
- APN_TYPE_XCAP,
- };
+ // /** APN type for enterprise */
+ // public static final String APN_TYPE_ENTERPRISE = ApnSetting.TYPE_ENTERPRISE_STRING;
public static final int RIL_CARD_MAX_APPS = 8;
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 0b7a398..9bd639b 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -49,6 +49,12 @@
compile_dex: true,
default_to_stubs: true,
+
+ // Additional hiddenapi annotations are provided in a separate module.
+ // TODO(b/180295980) - investigate whether this can be removed
+ hiddenapi_additional_annotations: [
+ "android.test.base-hiddenapi-annotations",
+ ],
}
// Build the android.test.base_static library
@@ -91,8 +97,9 @@
// ===============================================
// This contains the android.test classes from android.test.base plus
// the com.android.internal.util.Predicate[s] classes. This is only
-// intended for inclusion in android.test.legacy and must not be used
-// elsewhere.
+// intended for inclusion in android.test.legacy and in
+// android.test.base-hiddenapi-annotations to avoid a dependency cycle and must
+// not be used elsewhere.
java_library_static {
name: "android.test.base-minus-junit",
diff --git a/test-base/hiddenapi/Android.bp b/test-base/hiddenapi/Android.bp
index d4f52d0..1466590 100644
--- a/test-base/hiddenapi/Android.bp
+++ b/test-base/hiddenapi/Android.bp
@@ -14,11 +14,6 @@
// limitations under the License.
//
-// Provided solely to contribute information about which hidden parts of the android.test.base
-// library are used by apps. The source files are stubs of the actual files in ../src which use the
-// UnsupportedAppUsage annotation to tag those methods that are accessible via the hiddenapi.
-// Relies on the convention that modules with name <x>-hiddenapi provide hiddenapi information for
-// module <x> that is on the bootclasspath.
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -28,14 +23,20 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+// Provided solely to contribute information about which hidden parts of the android.test.base
+// library are used by apps. The source files are stubs of the actual files in ../src which use the
+// UnsupportedAppUsage annotation to tag those methods that are accessible via the hiddenapi.
java_library {
- name: "android.test.base-hiddenapi",
+ name: "android.test.base-hiddenapi-annotations",
compile_dex: true,
srcs: ["src/**/*.java"],
libs: [
- "android.test.base",
+ // Use this instead of `android.test.base` to avoid a dependency cycle
+ // as `android.test.base` depends on this.
+ "android.test.base-minus-junit",
+ "junit",
"unsupportedappusage",
],
}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index ab3572b..d96005b 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -95,10 +95,13 @@
new AddFsVerityCertRule(this, CERT_PATH);
private ITestDevice mDevice;
+ private boolean mDmRequireFsVerity;
@Before
public void setUp() throws DeviceNotAvailableException {
mDevice = getDevice();
+ mDmRequireFsVerity = "true".equals(
+ mDevice.getProperty("pm.dexopt.dm.require_fsverity"));
uninstallPackage(TARGET_PACKAGE);
}
@@ -124,7 +127,7 @@
verifyInstalledFiles(
INSTALLED_BASE_APK,
INSTALLED_BASE_APK_FSV_SIG);
- verifyInstalledFilesHaveFsverity();
+ verifyInstalledFilesHaveFsverity(INSTALLED_BASE_APK);
}
@Test
@@ -151,7 +154,9 @@
INSTALLED_BASE_APK_FSV_SIG,
INSTALLED_SPLIT_APK,
INSTALLED_SPLIT_APK_FSV_SIG);
- verifyInstalledFilesHaveFsverity();
+ verifyInstalledFilesHaveFsverity(
+ INSTALLED_BASE_APK,
+ INSTALLED_SPLIT_APK);
}
@Test
@@ -167,7 +172,9 @@
INSTALLED_BASE_APK_FSV_SIG,
INSTALLED_BASE_DM,
INSTALLED_BASE_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity();
+ verifyInstalledFilesHaveFsverity(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_DM);
}
@Test
@@ -189,7 +196,11 @@
INSTALLED_SPLIT_APK_FSV_SIG,
INSTALLED_SPLIT_DM,
INSTALLED_SPLIT_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity();
+ verifyInstalledFilesHaveFsverity(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_DM,
+ INSTALLED_SPLIT_APK,
+ INSTALLED_SPLIT_DM);
}
@Test
@@ -213,7 +224,9 @@
INSTALLED_BASE_APK_FSV_SIG,
INSTALLED_SPLIT_APK,
INSTALLED_SPLIT_APK_FSV_SIG);
- verifyInstalledFilesHaveFsverity();
+ verifyInstalledFilesHaveFsverity(
+ INSTALLED_BASE_APK,
+ INSTALLED_SPLIT_APK);
}
@Test
@@ -250,18 +263,6 @@
INSTALLED_BASE_APK,
INSTALLED_SPLIT_APK,
INSTALLED_SPLIT_APK_FSV_SIG);
-
- }
-
- @Test
- public void testInstallOnlyBaseHasFsvSig()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .addFile(BASE_APK_DM)
- .addFile(SPLIT_APK)
- .addFile(SPLIT_APK_DM)
- .runExpectingFailure();
}
@Test
@@ -271,18 +272,83 @@
.addFile(BASE_APK)
.addFileAndSignature(BASE_APK_DM)
.addFile(SPLIT_APK)
- .addFile(SPLIT_APK_DM)
+ .addFileAndSignature(SPLIT_APK_DM)
+ .run();
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_DM,
+ INSTALLED_BASE_DM_FSV_SIG,
+ INSTALLED_SPLIT_APK,
+ INSTALLED_SPLIT_DM,
+ INSTALLED_SPLIT_DM_FSV_SIG);
+ verifyInstalledFilesHaveFsverity(
+ INSTALLED_BASE_DM,
+ INSTALLED_SPLIT_DM);
+ }
+
+ @Test
+ public void testInstallDmWithoutFsvSig_Base()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ InstallMultiple installer = new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK_DM)
+ .addFile(SPLIT_APK)
+ .addFileAndSignature(SPLIT_APK_DM);
+ if (mDmRequireFsVerity) {
+ installer.runExpectingFailure();
+ } else {
+ installer.run();
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_DM,
+ INSTALLED_SPLIT_APK,
+ INSTALLED_SPLIT_DM,
+ INSTALLED_SPLIT_DM_FSV_SIG);
+ verifyInstalledFilesHaveFsverity(INSTALLED_SPLIT_DM);
+ }
+ }
+
+ @Test
+ public void testInstallDmWithoutFsvSig_Split()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ InstallMultiple installer = new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFileAndSignature(BASE_APK_DM)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK_DM);
+ if (mDmRequireFsVerity) {
+ installer.runExpectingFailure();
+ } else {
+ installer.run();
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_DM,
+ INSTALLED_BASE_DM_FSV_SIG,
+ INSTALLED_SPLIT_APK,
+ INSTALLED_SPLIT_DM);
+ verifyInstalledFilesHaveFsverity(INSTALLED_BASE_DM);
+ }
+ }
+
+ @Test
+ public void testInstallSomeApkIsMissingFsvSig_Base()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFileAndSignature(BASE_APK)
+ .addFileAndSignature(BASE_APK_DM)
+ .addFile(SPLIT_APK)
+ .addFileAndSignature(SPLIT_APK_DM)
.runExpectingFailure();
}
@Test
- public void testInstallOnlySplitHasFsvSig()
+ public void testInstallSomeApkIsMissingFsvSig_Split()
throws DeviceNotAvailableException, FileNotFoundException {
new InstallMultiple()
.addFile(BASE_APK)
- .addFile(BASE_APK_DM)
+ .addFileAndSignature(BASE_APK_DM)
.addFileAndSignature(SPLIT_APK)
- .addFile(SPLIT_APK_DM)
+ .addFileAndSignature(SPLIT_APK_DM)
.runExpectingFailure();
}
@@ -383,37 +449,36 @@
}
}
- private void verifyInstalledFilesHaveFsverity() throws DeviceNotAvailableException {
+ private void verifyInstalledFilesHaveFsverity(String... filenames)
+ throws DeviceNotAvailableException {
// Verify that all files are protected by fs-verity
String apkPath = getApkPath(TARGET_PACKAGE);
String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
long kTargetOffset = 0;
- for (String basename : expectRemoteCommandToSucceed("ls " + appDir).split("\n")) {
- if (basename.endsWith(".apk") || basename.endsWith(".dm")) {
- String path = appDir + "/" + basename;
- damageFileAgainstBlockDevice(path, kTargetOffset);
+ for (String basename : filenames) {
+ String path = appDir + "/" + basename;
+ damageFileAgainstBlockDevice(path, kTargetOffset);
- // Retry is sometimes needed to pass the test. Package manager may have FD leaks
- // (see b/122744005 as example) that prevents the file in question to be evicted
- // from filesystem cache. Forcing GC workarounds the problem.
- int retry = 5;
- for (; retry > 0; retry--) {
- BlockDeviceWriter.dropCaches(mDevice);
- if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) {
- break;
- }
- try {
- CLog.d("lsof: " + expectRemoteCommandToSucceed("lsof " + apkPath));
- Thread.sleep(1000);
- String pid = expectRemoteCommandToSucceed("pidof system_server");
- mDevice.executeShellV2Command("kill -10 " + pid); // force GC
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return;
- }
+ // Retry is sometimes needed to pass the test. Package manager may have FD leaks
+ // (see b/122744005 as example) that prevents the file in question to be evicted
+ // from filesystem cache. Forcing GC workarounds the problem.
+ int retry = 5;
+ for (; retry > 0; retry--) {
+ BlockDeviceWriter.dropCaches(mDevice);
+ if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) {
+ break;
}
- assertTrue("Read from " + path + " should fail", retry > 0);
+ try {
+ CLog.d("lsof: " + expectRemoteCommandToSucceed("lsof " + apkPath));
+ Thread.sleep(1000);
+ String pid = expectRemoteCommandToSucceed("pidof system_server");
+ mDevice.executeShellV2Command("kill -10 " + pid); // force GC
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
}
+ assertTrue("Read from " + path + " should fail", retry > 0);
}
}
diff --git a/tests/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp
index 58ccec7..5233a5b 100644
--- a/tests/BatteryStatsPerfTest/Android.bp
+++ b/tests/BatteryStatsPerfTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BatteryStatsPerfTests",
srcs: ["src/**/*.java"],
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 c5447c1..7d29cdd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,382 +17,91 @@
package com.android.server.wm.flicker
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.EventLogAssertionBuilder
-import com.android.server.wm.flicker.dsl.EventLogAssertionBuilderLegacy
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
-import com.android.server.wm.flicker.dsl.WmAssertionBuilder
-import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_WINDOW_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME
const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
const val WALLPAPER_TITLE = "Wallpaper"
-@JvmOverloads
-fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(bugId: Int = 0) {
- all("statusBarWindowIsAlwaysVisible", bugId) {
+fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
+ assertWm {
+ this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
+ }
+}
+
+fun FlickerTestParameter.navBarWindowIsAlwaysVisible() {
+ assertWm {
this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
}
}
@JvmOverloads
-fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(bugId: Int = 0) {
- all("navBarWindowIsAlwaysVisible", bugId) {
- this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
- }
-}
-
-fun WmAssertionBuilder.visibleWindowsShownMoreThanOneConsecutiveEntry(
- ignoreWindows: List<String> = emptyList(),
- bugId: Int = 0
+fun FlickerTestParameter.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ ignoreWindows: List<String> = emptyList()
) {
- all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId) {
+ assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
}
}
-fun WmAssertionBuilder.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
- all("launcherReplacesAppWindowAsTopWindow", bugId) {
- this.showsAppWindowOnTop(testApp.getPackage())
- .then()
- .showsAppWindowOnTop("Launcher")
- }
-}
-
-fun WmAssertionBuilder.wallpaperWindowBecomesVisible(bugId: Int = 0) {
- all("wallpaperWindowBecomesVisible", bugId) {
- this.hidesBelowAppWindow(WALLPAPER_TITLE)
- .then()
- .showsBelowAppWindow(WALLPAPER_TITLE)
- }
-}
-
-fun WmAssertionBuilder.wallpaperWindowBecomesInvisible(bugId: Int = 0) {
- all("wallpaperWindowBecomesInvisible", bugId) {
- this.showsBelowAppWindow("Wallpaper")
- .then()
- .hidesBelowAppWindow("Wallpaper")
- }
-}
-
-fun WmAssertionBuilder.appWindowAlwaysVisibleOnTop(
- packageName: String,
- bugId: Int = 0
-) {
- all("appWindowAlwaysVisibleOnTop", bugId) {
- this.showsAppWindowOnTop(packageName)
- }
-}
-
-fun WmAssertionBuilder.appWindowBecomesVisible(appName: String, bugId: Int = 0) {
- all("appWindowBecomesVisible", bugId) {
- this.hidesAppWindow(appName)
- .then()
- .showsAppWindow(appName)
- }
-}
-
-fun WmAssertionBuilder.appWindowBecomesInVisible(appName: String, bugId: Int = 0) {
- all("appWindowBecomesInVisible", bugId) {
- this.showsAppWindow(appName)
- .then()
- .hidesAppWindow(appName)
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilder.noUncoveredRegions(
- beginRotation: Int,
- endRotation: Int = beginRotation,
- allStates: Boolean = true,
- bugId: Int = 0
-) {
- val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
- val endingBounds = WindowUtils.getDisplayBounds(endRotation)
- if (allStates) {
- all("noUncoveredRegions", bugId) {
- if (startingBounds == endingBounds) {
- this.coversAtLeastRegion(startingBounds)
- } else {
- this.coversAtLeastRegion(startingBounds)
- .then()
- .coversAtLeastRegion(endingBounds)
- }
- }
- } else {
- start("noUncoveredRegions_StartingPos") {
- this.coversAtLeastRegion(startingBounds)
- }
- end("noUncoveredRegions_EndingPos") {
- this.coversAtLeastRegion(endingBounds)
- }
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible(
- rotatesScreen: Boolean = false,
- bugId: Int = 0
-) {
- if (rotatesScreen) {
- all("navBarLayerIsAlwaysVisible", bugId) {
- this.showsLayer(NAV_BAR_LAYER_NAME)
- .then()
- .hidesLayer(NAV_BAR_LAYER_NAME)
- .then()
- .showsLayer(NAV_BAR_LAYER_NAME)
- }
- } else {
- all("navBarLayerIsAlwaysVisible", bugId) {
- this.showsLayer(NAV_BAR_LAYER_NAME)
- }
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible(
- rotatesScreen: Boolean = false,
- bugId: Int = 0
-) {
- if (rotatesScreen) {
- all("statusBarLayerIsAlwaysVisible", bugId) {
- this.showsLayer(STATUS_BAR_WINDOW_NAME)
- .then()
- hidesLayer(STATUS_BAR_WINDOW_NAME)
- .then()
- .showsLayer(STATUS_BAR_WINDOW_NAME)
- }
- } else {
- all("statusBarLayerIsAlwaysVisible", bugId) {
- this.showsLayer(STATUS_BAR_WINDOW_NAME)
- }
- }
-}
-
-@JvmOverloads
-fun LayersAssertionBuilder.navBarLayerRotatesAndScales(
- beginRotation: Int,
- endRotation: Int = beginRotation,
- bugId: Int = 0
-) {
- val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
- val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
-
- start("navBarLayerRotatesAndScales_StartingPos", bugId) {
- this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
- }
- end("navBarLayerRotatesAndScales_EndingPost", bugId) {
- this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos)
- }
-
- /*if (startingPos == endingPos) {
- all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
- this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
- }
- }*/
-}
-
-@JvmOverloads
-fun LayersAssertionBuilder.statusBarLayerRotatesScales(
- beginRotation: Int,
- endRotation: Int = beginRotation,
- bugId: Int = 0
-) {
- val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
- val endingPos = WindowUtils.getStatusBarPosition(endRotation)
-
- start("statusBarLayerRotatesScales_StartingPos", bugId) {
- this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos)
- }
- end("statusBarLayerRotatesScales_EndingPos", bugId) {
- this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos)
- }
-}
-
-fun LayersAssertionBuilder.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers: List<String> = emptyList(),
- bugId: Int = 0
-) {
- all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId) {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers)
- }
-}
-
-fun LayersAssertionBuilder.appLayerReplacesWallpaperLayer(appName: String, bugId: Int = 0) {
- all("appLayerReplacesWallpaperLayer", bugId) {
- this.showsLayer("Wallpaper")
- .then()
- .replaceVisibleLayer("Wallpaper", appName)
- }
-}
-
-fun LayersAssertionBuilder.wallpaperLayerReplacesAppLayer(testApp: IAppHelper, bugId: Int = 0) {
- all("appLayerReplacesWallpaperLayer", bugId) {
- this.showsLayer(testApp.getPackage())
- .then()
- .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
- }
-}
-
-fun LayersAssertionBuilder.layerAlwaysVisible(packageName: String, bugId: Int = 0) {
- all("layerAlwaysVisible", bugId) {
- this.showsLayer(packageName)
- }
-}
-
-fun LayersAssertionBuilder.layerBecomesVisible(packageName: String, bugId: Int = 0) {
- all("layerBecomesVisible", bugId) {
- this.hidesLayer(packageName)
- .then()
- .showsLayer(packageName)
- }
-}
-
-fun LayersAssertionBuilder.layerBecomesInvisible(packageName: String, bugId: Int = 0) {
- all("layerBecomesInvisible", bugId) {
- this.showsLayer(packageName)
- .then()
- .hidesLayer(packageName)
- }
-}
-
-fun EventLogAssertionBuilder.focusChanges(vararg windows: String, bugId: Int = 0) {
- all("focusChanges", bugId) {
- this.focusChanges(windows)
- }
-}
-
-fun EventLogAssertionBuilder.focusDoesNotChange(bugId: Int = 0) {
- all("focusDoesNotChange", bugId) {
- this.focusDoesNotChange()
- }
-}
-
-@JvmOverloads
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
- }
-}
-
-@JvmOverloads
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("navBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
- }
-}
-
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
- ignoreWindows: List<String> = emptyList(),
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId, enabled) {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
- }
-}
-
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) {
+fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) {
+ assertWm {
this.showsAppWindowOnTop(testApp.getPackage())
.then()
.showsAppWindowOnTop("Launcher")
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("wallpaperWindowBecomesVisible", bugId, enabled) {
+fun FlickerTestParameter.wallpaperWindowBecomesVisible() {
+ assertWm {
this.hidesBelowAppWindow(WALLPAPER_TITLE)
.then()
.showsBelowAppWindow(WALLPAPER_TITLE)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("wallpaperWindowBecomesInvisible", bugId, enabled) {
+fun FlickerTestParameter.wallpaperWindowBecomesInvisible() {
+ assertWm {
this.showsBelowAppWindow("Wallpaper")
.then()
.hidesBelowAppWindow("Wallpaper")
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
- packageName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("appWindowAlwaysVisibleOnTop", bugId, enabled) {
+fun FlickerTestParameter.appWindowAlwaysVisibleOnTop(packageName: String) {
+ assertWm {
this.showsAppWindowOnTop(packageName)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
- appName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("appWindowBecomesVisible", bugId, enabled) {
+fun FlickerTestParameter.appWindowBecomesVisible(appName: String) {
+ assertWm {
this.hidesAppWindow(appName)
.then()
.showsAppWindow(appName)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
- appName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("appWindowBecomesInVisible", bugId, enabled) {
+fun FlickerTestParameter.appWindowBecomesInVisible(appName: String) {
+ assertWm {
this.showsAppWindow(appName)
.then()
.hidesAppWindow(appName)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
-fun LayersAssertionBuilderLegacy.noUncoveredRegions(
+fun FlickerTestParameter.noUncoveredRegions(
beginRotation: Int,
endRotation: Int = beginRotation,
- allStates: Boolean = true,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
+ allStates: Boolean = true
) {
val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
val endingBounds = WindowUtils.getDisplayBounds(endRotation)
if (allStates) {
- all("noUncoveredRegions", bugId, enabled) {
+ assertLayers {
if (startingBounds == endingBounds) {
this.coversAtLeastRegion(startingBounds)
} else {
@@ -402,24 +111,19 @@
}
}
} else {
- start("noUncoveredRegions_StartingPos") {
+ assertLayersStart {
this.coversAtLeastRegion(startingBounds)
}
- end("noUncoveredRegions_EndingPos") {
+ assertLayersEnd {
this.coversAtLeastRegion(endingBounds)
}
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
-fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible(
- rotatesScreen: Boolean = false,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
+fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
- all("navBarLayerIsAlwaysVisible", bugId, enabled) {
+ assertLayers {
this.showsLayer(NAV_BAR_LAYER_NAME)
.then()
.hidesLayer(NAV_BAR_LAYER_NAME)
@@ -427,169 +131,116 @@
.showsLayer(NAV_BAR_LAYER_NAME)
}
} else {
- all("navBarLayerIsAlwaysVisible", bugId, enabled) {
+ assertLayers {
this.showsLayer(NAV_BAR_LAYER_NAME)
}
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
-fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible(
- rotatesScreen: Boolean = false,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
+fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
- all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(STATUS_BAR_LAYER_NAME)
+ assertLayers {
+ this.showsLayer(STATUS_BAR_WINDOW_NAME)
.then()
- .hidesLayer(STATUS_BAR_LAYER_NAME)
+ hidesLayer(STATUS_BAR_WINDOW_NAME)
.then()
- .showsLayer(STATUS_BAR_LAYER_NAME)
+ .showsLayer(STATUS_BAR_WINDOW_NAME)
}
} else {
- all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(STATUS_BAR_LAYER_NAME)
+ assertLayers {
+ this.showsLayer(STATUS_BAR_WINDOW_NAME)
}
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
-fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales(
+fun FlickerTestParameter.navBarLayerRotatesAndScales(
beginRotation: Int,
- endRotation: Int = beginRotation,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
+ endRotation: Int = beginRotation
) {
val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
- start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) {
+ assertLayersStart {
this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
}
- end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) {
+ assertLayersEnd {
this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos)
}
-
- if (startingPos == endingPos) {
- all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
- this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
- }
- }
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
-fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales(
+fun FlickerTestParameter.statusBarLayerRotatesScales(
beginRotation: Int,
- endRotation: Int = beginRotation,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
+ endRotation: Int = beginRotation
) {
val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
- start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) {
- this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, startingPos)
+ assertLayersStart {
+ this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos)
}
- end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) {
- this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, endingPos)
+ assertLayersEnd {
+ this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers: List<String> = kotlin.collections.emptyList(),
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
+@JvmOverloads
+fun FlickerTestParameter.visibleLayersShownMoreThanOneConsecutiveEntry(
+ ignoreLayers: List<String> = emptyList()
) {
- all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId, enabled) {
+ assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer(
- appName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("appLayerReplacesWallpaperLayer", bugId, enabled) {
+fun FlickerTestParameter.appLayerReplacesWallpaperLayer(appName: String) {
+ assertLayers {
this.showsLayer("Wallpaper")
.then()
.replaceVisibleLayer("Wallpaper", appName)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("appLayerReplacesWallpaperLayer", bugId, enabled) {
+fun FlickerTestParameter.wallpaperLayerReplacesAppLayer(testApp: IAppHelper) {
+ assertLayers {
this.showsLayer(testApp.getPackage())
.then()
.replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun LayersAssertionBuilderLegacy.layerAlwaysVisible(
- packageName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("layerAlwaysVisible", bugId, enabled) {
+fun FlickerTestParameter.layerAlwaysVisible(packageName: String) {
+ assertLayers {
this.showsLayer(packageName)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun LayersAssertionBuilderLegacy.layerBecomesVisible(
- packageName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("layerBecomesVisible", bugId, enabled) {
+fun FlickerTestParameter.layerBecomesVisible(packageName: String) {
+ assertLayers {
this.hidesLayer(packageName)
.then()
.showsLayer(packageName)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun LayersAssertionBuilderLegacy.layerBecomesInvisible(
- packageName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("layerBecomesInvisible", bugId, enabled) {
+fun FlickerTestParameter.layerBecomesInvisible(packageName: String) {
+ assertLayers {
this.showsLayer(packageName)
.then()
.hidesLayer(packageName)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun EventLogAssertionBuilderLegacy.focusChanges(
- vararg windows: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("focusChanges", bugId, enabled) {
+fun FlickerTestParameter.focusChanges(vararg windows: String) {
+ assertEventLog {
this.focusChanges(windows)
}
}
-@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
-fun EventLogAssertionBuilderLegacy.focusDoesNotChange(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("focusDoesNotChange", bugId, enabled) {
+fun FlickerTestParameter.focusDoesNotChange() {
+ assertEventLog {
this.focusDoesNotChange()
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index c507841..fbf18d4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,11 +16,17 @@
package com.android.server.wm.flicker.close
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
@@ -35,12 +41,12 @@
import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -51,86 +57,119 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppBackButtonTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class CloseAppBackButtonTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = SimpleAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ this.setRotation(testSpec.config.startRotation)
+ testApp.launchViaIntent(wmHelper)
+ }
+ }
+ transitions {
+ device.pressBack()
+ wmHelper.waitForHomeActivityVisible()
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
+ }
+ test {
+ testApp.exit()
+ }
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun launcherReplacesAppWindowAsTopWindow() =
+ testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+
+ @Presubmit
+ @Test
+ fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest(bugId = 173684672)
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 173684672)
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- this.setRotation(configuration.startRotation)
- testApp.launchViaIntent(wmHelper)
- }
- }
- transitions {
- device.pressBack()
- wmHelper.waitForHomeActivityVisible()
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
- }
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- launcherReplacesAppWindowAsTopWindow(testApp)
- wallpaperWindowBecomesVisible()
- }
-
- layersTrace {
- noUncoveredRegions(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- wallpaperLayerReplacesAppLayer(testApp)
-
- if (!isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
- }
- }
- }
-
- flaky {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672)
- }
-
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672)
-
- if (isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
- }
- }
- }
- }
- }
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index d1c3efe..08d2b7c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,12 +16,17 @@
package com.android.server.wm.flicker.close
+import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
@@ -36,12 +41,12 @@
import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -50,88 +55,121 @@
* Test app closes by pressing home button.
* To run this test: `atest FlickerTests:CloseAppHomeButtonTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppHomeButtonTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class CloseAppHomeButtonTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = SimpleAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ transitions {
+ device.pressHome()
+ wmHelper.waitForHomeActivityVisible()
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
+ }
+ test {
+ testApp.exit()
+ }
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun launcherReplacesAppWindowAsTopWindow() =
+ testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+
+ @Presubmit
+ @Test
+ fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(
+ testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest(bugId = 173689015)
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 173689015)
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.startRotation)
- }
- }
- transitions {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
- }
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- launcherReplacesAppWindowAsTopWindow(testApp)
- wallpaperWindowBecomesVisible()
- }
-
- layersTrace {
- noUncoveredRegions(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- wallpaperLayerReplacesAppLayer(testApp)
-
- if (!isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
- }
- }
- }
-
- flaky {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015)
- }
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015)
-
- if (isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
- }
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
}
}
}
\ No newline at end of file
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 323236e..f7e7493 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,13 +16,9 @@
package com.android.server.wm.flicker.helpers
-import android.os.Bundle
import android.os.RemoteException
-import android.platform.helpers.IAppHelper
import android.view.Surface
import com.android.server.wm.flicker.Flicker
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.startRotation
/**
* Changes the device [rotation] and wait for the rotation animation to complete
@@ -47,128 +43,4 @@
} catch (e: RemoteException) {
throw RuntimeException(e)
}
-}
-
-/**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param beginRotation Initial screen rotation
- * @param endRotation End screen rotation (if any, otherwise use same as initial)
- *
- * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
-</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
-fun buildTestTag(
- testName: String,
- app: IAppHelper,
- beginRotation: Int,
- endRotation: Int
-): String {
- return buildTestTag(
- testName, app.launcherName, beginRotation, endRotation, app2 = null, extraInfo = "")
-}
-
-/**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param configuration Configuration for the test
- * @param extraInfo Additional information to append to the tag
- *
- * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
-</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
-@JvmOverloads
-fun buildTestTag(
- testName: String,
- configuration: Bundle,
- extraInfo: String = ""
-): String {
- return buildTestTag(testName,
- app = null,
- beginRotation = configuration.startRotation,
- endRotation = configuration.endRotation,
- app2 = null,
- extraInfo = extraInfo)
-}
-
-/**
- * Build a test tag for the test
- * @param configuration Configuration for the test
- * @param extraInfo Additional information to append to the tag
- *
- * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
-</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
-@JvmOverloads
-fun buildTestTag(
- configuration: Bundle,
- extraInfo: String = ""
-): String {
- return buildTestTag(testName = null,
- app = null,
- beginRotation = configuration.startRotation,
- endRotation = configuration.endRotation,
- app2 = null,
- extraInfo = extraInfo)
-}
-
-/**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param configuration Configuration for the test
- * @param extraInfo Additional information to append to the tag
- *
- * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
-</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
-@JvmOverloads
-fun buildTestTag(
- testName: String,
- app: IAppHelper?,
- configuration: Bundle,
- extraInfo: String = ""
-): String {
- return buildTestTag(testName, app?.launcherName ?: "", configuration.startRotation,
- configuration.endRotation, app2 = null, extraInfo = extraInfo)
-}
-
-/**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param app2 Second app being launched (if any)
- * @param beginRotation Initial screen rotation
- * @param endRotation End screen rotation (if any, otherwise use same as initial)
- * @param extraInfo Additional information to append to the tag
- *
- * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
-</EXTRA></NAME> */
-fun buildTestTag(
- testName: String?,
- app: String?,
- beginRotation: Int,
- endRotation: Int,
- app2: String?,
- extraInfo: String
-): String {
- var testTag = ""
- if (testName != null) {
- testTag += testName
- }
- if (app != null) {
- testTag += "__$app"
- }
- if (app2 != null) {
- testTag += "-$app2"
- }
- testTag += "__${Surface.rotationToString(beginRotation)}"
- if (endRotation != beginRotation) {
- testTag += "-${Surface.rotationToString(endRotation)}"
- }
- if (extraInfo.isNotEmpty()) {
- testTag += "__$extraInfo"
- }
-
- if (testTag.startsWith("__")) {
- testTag = testTag.drop(2)
- }
- return testTag
-}
+}
\ No newline at end of file
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 c7736f8..47eaddf 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
@@ -16,14 +16,19 @@
package com.android.server.wm.flicker.ime
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
@@ -37,7 +42,9 @@
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -48,79 +55,116 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeAutoOpenWindowToAppTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(device, wmHelper)
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ teardown {
+ test {
+ testApp.exit()
+ wmHelper.waitForAppTransitionIdle()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ testApp.closeIME(device, wmHelper)
+ }
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+
+ @Postsubmit
+ @Test
+ fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+
+ @Postsubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+
+ @Postsubmit
+ @Test
+ fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
+
+ @Postsubmit
+ @Test
+ fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation, repetitions = 5) { configuration ->
- val testApp = ImeAppAutoFocusHelper(instrumentation,
- configuration.startRotation)
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- this.setRotation(configuration.startRotation)
- }
- }
- teardown {
- test {
- testApp.exit()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- testApp.closeIME(device, wmHelper)
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- postsubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE))
- imeAppWindowIsAlwaysVisible(testApp)
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation)
- imeLayerBecomesInvisible()
- imeAppLayerIsAlwaysVisible(testApp)
- if (!isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation)
- statusBarLayerRotatesScales(configuration.startRotation)
- }
- }
- }
-
- flaky {
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry()
-
- if (isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation)
- statusBarLayerRotatesScales(configuration.startRotation)
- }
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
}
}
-}
\ No newline at end of file
+}
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 aa24456..38a88d3 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
@@ -16,14 +16,18 @@
package com.android.server.wm.flicker.ime
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
@@ -37,7 +41,9 @@
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -48,93 +54,151 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeAutoOpenWindowToHomeTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(device, wmHelper)
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ teardown {
+ test {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ device.pressHome()
+ wmHelper.waitForHomeActivityVisible()
+ wmHelper.waitImeWindowGone()
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+
+ @Presubmit
+ @Test
+ fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+
+ @Presubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerIsAlwaysVisible_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerIsAlwaysVisible_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ }
+
+ @FlakyTest
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation, repetitions = 5) { configuration ->
- val testApp = ImeAppAutoFocusHelper(instrumentation,
- configuration.startRotation)
- withTestName {
- buildTestTag(configuration)
- }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- this.setRotation(configuration.startRotation)
- }
- }
- teardown {
- test {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- wmHelper.waitImeWindowGone()
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE))
-
- imeWindowBecomesInvisible()
- imeAppWindowBecomesInvisible(testApp)
- }
-
- layersTrace {
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
- imeLayerBecomesInvisible()
- imeAppLayerBecomesInvisible(testApp)
-
- if (!isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE))
- }
- }
- }
-
- flaky {
- layersTrace {
- if (isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE))
- }
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
}
}
}
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 2bd5abb..476708c 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
@@ -16,13 +16,18 @@
package com.android.server.wm.flicker.ime
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -33,10 +38,10 @@
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.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -47,63 +52,97 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeWindowToAppTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = ImeAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.launchViaIntent()
+ this.setRotation(testSpec.config.startRotation)
+ }
+ eachRun {
+ testApp.openIME(device, wmHelper)
+ }
+ }
+ teardown {
+ test {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ testApp.closeIME(device, wmHelper)
+ }
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+
+ @Postsubmit
+ @Test
+ fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+
+ @Postsubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+
+ @Postsubmit
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Postsubmit
+ @Test
+ fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
+
+ @Postsubmit
+ @Test
+ fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = ImeAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- testApp.launchViaIntent()
- this.setRotation(configuration.startRotation)
- }
- eachRun {
- testApp.openIME(device, wmHelper)
- }
- }
- teardown {
- test {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- testApp.closeIME(device, wmHelper)
- }
- assertions {
- postsubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE))
- imeAppWindowIsAlwaysVisible(testApp)
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation)
- navBarLayerRotatesAndScales(configuration.startRotation)
- statusBarLayerRotatesScales(configuration.startRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry()
- imeLayerBecomesInvisible()
- imeAppLayerIsAlwaysVisible(testApp)
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
}
}
}
\ 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 7b61bb5..ac140f5 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
@@ -16,28 +16,33 @@
package com.android.server.wm.flicker.ime
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.isRotated
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.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.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.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -47,90 +52,126 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeWindowToHomeTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = ImeAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ this.setRotation(testSpec.config.startRotation)
+ testApp.openIME(device, wmHelper)
+ }
+ }
+ transitions {
+ device.pressHome()
+ wmHelper.waitForHomeActivityVisible()
+ wmHelper.waitImeWindowGone()
+ }
+ teardown {
+ eachRun {
+ device.pressHome()
+ wmHelper.waitForHomeActivityVisible()
+ }
+ test {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+
+ @Postsubmit
+ @Test
+ fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
+
+ @Postsubmit
+ @Test
+ fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+
+ @Postsubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ Surface.ROTATION_0)
+
+ @Postsubmit
+ @Test
+ fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
+
+ @Postsubmit
+ @Test
+ fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+
+ @Postsubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Postsubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = ImeAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.startRotation)
- testApp.openIME(device, wmHelper)
- }
- }
- transitions {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- wmHelper.waitImeWindowGone()
- }
- teardown {
- eachRun {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- }
- test {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- postsubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE))
- imeWindowBecomesInvisible()
- imeAppWindowBecomesInvisible(testApp)
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- imeLayerBecomesInvisible()
- imeAppLayerBecomesInvisible(testApp)
- noUncoveredRegions(configuration.startRotation,
- Surface.ROTATION_0)
-
- if (!isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
- }
- }
- }
-
- flaky {
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE))
-
- if (isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0)
- }
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index cfdd856..212644c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -17,85 +17,75 @@
package com.android.server.wm.flicker.ime
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
-import com.android.server.wm.flicker.dsl.WmAssertionBuilder
+import com.android.server.wm.flicker.FlickerTestParameter
const val IME_WINDOW_TITLE = "InputMethod"
-@JvmOverloads
-fun LayersAssertionBuilder.imeLayerBecomesVisible(bugId: Int = 0) {
- all("imeLayerBecomesVisible", bugId) {
+fun FlickerTestParameter.imeLayerBecomesVisible() {
+ assertLayers {
this.hidesLayer(IME_WINDOW_TITLE)
- .then()
- .showsLayer(IME_WINDOW_TITLE)
+ .then()
+ .showsLayer(IME_WINDOW_TITLE)
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.imeLayerBecomesInvisible(bugId: Int = 0) {
- all("imeLayerBecomesInvisible", bugId) {
+fun FlickerTestParameter.imeLayerBecomesInvisible() {
+ assertLayers {
this.showsLayer(IME_WINDOW_TITLE)
- .then()
- .hidesLayer(IME_WINDOW_TITLE)
+ .then()
+ .hidesLayer(IME_WINDOW_TITLE)
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.imeAppLayerIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
- all("imeAppLayerIsAlwaysVisible", bugId) {
+fun FlickerTestParameter.imeAppLayerIsAlwaysVisible(testApp: IAppHelper) {
+ assertLayers {
this.showsLayer(testApp.getPackage())
}
}
-@JvmOverloads
-fun WmAssertionBuilder.imeAppWindowIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
- all("imeAppWindowIsAlwaysVisible", bugId) {
+fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(testApp: IAppHelper) {
+ assertWm {
this.showsAppWindowOnTop(testApp.getPackage())
}
}
-@JvmOverloads
-fun WmAssertionBuilder.imeWindowBecomesVisible(bugId: Int = 0) {
- all("imeWindowBecomesVisible", bugId) {
+fun FlickerTestParameter.imeWindowBecomesVisible() {
+ assertWm {
this.hidesNonAppWindow(IME_WINDOW_TITLE)
- .then()
- .showsNonAppWindow(IME_WINDOW_TITLE)
+ .then()
+ .showsNonAppWindow(IME_WINDOW_TITLE)
}
}
-@JvmOverloads
-fun WmAssertionBuilder.imeWindowBecomesInvisible(bugId: Int = 0) {
- all("imeWindowBecomesInvisible", bugId) {
+fun FlickerTestParameter.imeWindowBecomesInvisible() {
+ assertWm {
this.showsNonAppWindow(IME_WINDOW_TITLE)
- .then()
- .hidesNonAppWindow(IME_WINDOW_TITLE)
+ .then()
+ .hidesNonAppWindow(IME_WINDOW_TITLE)
}
}
-@JvmOverloads
-fun WmAssertionBuilder.imeAppWindowBecomesVisible(windowName: String, bugId: Int = 0) {
- all("imeAppWindowBecomesVisible", bugId) {
+fun FlickerTestParameter.imeAppWindowBecomesVisible(windowName: String) {
+ assertWm {
this.hidesAppWindow(windowName)
- .then()
- .showsAppWindow(windowName)
+ .then()
+ .showsAppWindow(windowName)
}
}
-@JvmOverloads
-fun WmAssertionBuilder.imeAppWindowBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
- all("imeAppWindowBecomesInvisible", bugId) {
+fun FlickerTestParameter.imeAppWindowBecomesInvisible(testApp: IAppHelper) {
+ assertWm {
this.showsAppWindowOnTop(testApp.getPackage())
- .then()
- .appWindowNotOnTop(testApp.getPackage())
+ .then()
+ .appWindowNotOnTop(testApp.getPackage())
}
}
-@JvmOverloads
-fun LayersAssertionBuilder.imeAppLayerBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
- all("imeAppLayerBecomesInvisible", bugId) {
+fun FlickerTestParameter.imeAppLayerBecomesInvisible(testApp: IAppHelper) {
+ assertLayers {
this.skipUntilFirstAssertion()
- .showsLayer(testApp.getPackage())
- .then()
- .hidesLayer(testApp.getPackage())
+ .showsLayer(testApp.getPackage())
+ .then()
+ .hidesLayer(testApp.getPackage())
}
}
\ No newline at end of file
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 9e94d6e..c7a5178 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
@@ -16,13 +16,17 @@
package com.android.server.wm.flicker.ime
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -32,14 +36,16 @@
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.helpers.isRotated
+import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.layerAlwaysVisible
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 org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -50,80 +56,119 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenImeWindowTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = ImeAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.launchViaIntent(wmHelper)
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ transitions {
+ testApp.openIME(device, wmHelper)
+ }
+ teardown {
+ eachRun {
+ testApp.closeIME(device, wmHelper)
+ }
+ test {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible()
+
+ @Postsubmit
+ @Test
+ fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
+
+ @Postsubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+
+ @Postsubmit
+ @Test
+ fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
+
+ @Postsubmit
+ @Test
+ fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`)
+
+ @Postsubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ }
+
+ @Postsubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = ImeAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.startRotation)
- }
- }
- transitions {
- testApp.openIME(device, wmHelper)
- }
- teardown {
- eachRun {
- testApp.closeIME(device, wmHelper)
- }
- test {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- postsubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
-
- imeWindowBecomesVisible()
- appWindowAlwaysVisibleOnTop(testApp.`package`)
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation)
- imeLayerBecomesVisible()
- layerAlwaysVisible(testApp.`package`)
-
- if (!isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation)
- statusBarLayerRotatesScales(configuration.startRotation)
- }
- }
- }
-
- flaky {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry()
-
- if (isRotated) {
- navBarLayerRotatesAndScales(configuration.startRotation)
- statusBarLayerRotatesScales(configuration.startRotation)
- }
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
}
}
}
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 2fe49af..0cd5d79 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
@@ -16,14 +16,18 @@
package com.android.server.wm.flicker.ime
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -31,18 +35,20 @@
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
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.helpers.isRotated
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.testapp.ActivityOptions
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -53,89 +59,132 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ReOpenImeWindowTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(device, wmHelper)
+ }
+ eachRun {
+ device.pressRecentApps()
+ wmHelper.waitImeWindowGone()
+ wmHelper.waitForAppTransitionIdle()
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ transitions {
+ device.reopenAppFromOverview(wmHelper)
+ wmHelper.waitImeWindowShown()
+ }
+ teardown {
+ test {
+ this.setRotation(Surface.ROTATION_0)
+ testApp.exit()
+ }
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun imeAppWindowBecomesVisible() =
+ testSpec.imeAppWindowBecomesVisible(testAppComponentName.className)
+
+ @Presubmit
+ @Test
+ // During testing the launcher is always in portrait mode
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ testSpec.config.endRotation)
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun appLayerReplacesWallpaperLayer() =
+ testSpec.appLayerReplacesWallpaperLayer(testAppComponentName.className)
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation, repetitions = 1) { configuration ->
- val testApp = ImeAppAutoFocusHelper(instrumentation,
- configuration.startRotation)
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- }
- eachRun {
- device.pressRecentApps()
- wmHelper.waitImeWindowGone()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(configuration.startRotation)
- }
- }
- transitions {
- device.reopenAppFromOverview()
- wmHelper.waitImeWindowShown()
- }
- teardown {
- test {
- this.setRotation(Surface.ROTATION_0)
- testApp.exit()
- }
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- imeWindowBecomesVisible()
- imeAppWindowBecomesVisible(testAppComponentName.className)
- wallpaperWindowBecomesInvisible()
- }
-
- layersTrace {
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
- statusBarLayerIsAlwaysVisible()
- navBarLayerIsAlwaysVisible()
- imeLayerBecomesVisible()
- appLayerReplacesWallpaperLayer(testAppComponentName.className)
-
- if (!isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- }
- }
- }
-
- flaky {
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry()
-
- if (isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- }
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 1)
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index be3fa5f..130860d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -17,12 +17,12 @@
package com.android.server.wm.flicker.launch
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.WmAssertionBuilder
+import com.android.server.wm.flicker.FlickerTestParameter
-fun WmAssertionBuilder.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
- all("appWindowReplacesLauncherAsTopWindow", bugId) {
+fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) {
+ assertWm {
this.showsAppWindowOnTop("Launcher")
- .then()
- .showsAppWindowOnTop("Snapshot", testApp.getPackage())
+ .then()
+ .showsAppWindowOnTop("Snapshot", testApp.getPackage())
}
}
\ No newline at end of file
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 0ec0b04..74f002d 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
@@ -16,14 +16,17 @@
package com.android.server.wm.flicker.launch
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -39,9 +42,11 @@
import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
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.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isRotated
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -52,85 +57,122 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class OpenAppColdTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = SimpleAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ // wmHelper.waitForFullScreenApp(testApp.component)
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ wmHelper.waitForAppTransitionIdle()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ fun appWindowReplacesLauncherAsTopWindow() =
+ testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+
+ @Presubmit
+ @Test
+ fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
+
+ @Presubmit
+ @Test
+ // During testing the launcher is always in portrait mode
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun appLayerReplacesWallpaperLayer() =
+ testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+
+ @FlakyTest
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation) { configuration ->
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- this.setRotation(configuration.startRotation)
- }
- }
- transitions {
- testApp.open()
- wmHelper.waitForFullScreenApp(testApp.component)
- }
- teardown {
- eachRun {
- testApp.exit()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible()
- }
-
- layersTrace {
- // During testing the launcher is always in portrait mode
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- appLayerReplacesWallpaperLayer(testApp.`package`)
-
- if (!isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- }
- }
-
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
- }
- }
-
- flaky {
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry()
-
- if (isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- }
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ 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 84cc8e3..e2a258a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,17 +16,22 @@
package com.android.server.wm.flicker.launch
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.endRotation
import com.android.server.wm.flicker.focusChanges
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.buildTestTag
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -40,9 +45,11 @@
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
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.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isRotated
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -53,97 +60,115 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromOverviewTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
+class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = SimpleAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.launchViaIntent(wmHelper)
+ }
+ eachRun {
+ device.pressHome()
+ wmHelper.waitForAppTransitionIdle()
+ device.pressRecentApps()
+ wmHelper.waitForAppTransitionIdle()
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ transitions {
+ device.reopenAppFromOverview(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
+ teardown {
+ test {
+ testApp.exit()
+ }
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Test
+ fun appWindowReplacesLauncherAsTopWindow() =
+ testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+
+ @Test
+ fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun appLayerReplacesWallpaperLayer() =
+ testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+
+ @Postsubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 141361128)
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0,
+ testSpec.config.endRotation)
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- testApp.launchViaIntent(wmHelper)
- }
- eachRun {
- device.pressHome()
- wmHelper.waitForAppTransitionIdle()
- device.pressRecentApps()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(configuration.startRotation)
- }
- }
- transitions {
- device.reopenAppFromOverview()
- wmHelper.waitForFullScreenApp(testApp.component)
- }
- teardown {
- test {
- testApp.exit()
- }
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible()
- }
-
- layersTrace {
- appLayerReplacesWallpaperLayer(testApp.`package`)
-
- if (!isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- } else {
- statusBarLayerIsAlwaysVisible()
- navBarLayerIsAlwaysVisible()
- }
- }
-
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
- }
- }
-
- postsubmit {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- }
-
- flaky {
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry()
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
- bugId = 141361128)
-
- if (isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- } else {
- statusBarLayerIsAlwaysVisible()
- navBarLayerIsAlwaysVisible()
- }
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
}
}
}
\ No newline at end of file
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 1f375a5..386dafc5 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
@@ -16,14 +16,17 @@
package com.android.server.wm.flicker.launch
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.FlickerBuilderProvider
+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.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -36,12 +39,13 @@
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
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.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isRotated
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -52,89 +56,122 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppWarmTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) {
+class OpenAppWarmTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = SimpleAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.launchViaIntent(wmHelper)
+ // wmHelper.waitForFullScreenApp(testApp.component)
+ }
+ eachRun {
+ device.pressHome()
+ wmHelper.waitForHomeActivityVisible()
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
+ }
+ test {
+ testApp.exit()
+ }
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ fun appWindowReplacesLauncherAsTopWindow() =
+ testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+
+ @Presubmit
+ @Test
+ fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
+
+ @Presubmit
+ @Test
+ // During testing the launcher is always in portrait mode
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun appLayerReplacesWallpaperLayer() =
+ testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
+
+ @Presubmit
+ @Test
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance()
- .buildTest(instrumentation) { configuration ->
- withTestName { buildTestTag(configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- testApp.open()
- wmHelper.waitForFullScreenApp(testApp.component)
- }
- eachRun {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- this.setRotation(configuration.startRotation)
- }
- }
- transitions {
- testApp.open()
- wmHelper.waitForFullScreenApp(testApp.component)
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
- }
- }
- assertions {
- val isRotated = configuration.startRotation.isRotated()
-
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible()
- }
-
- layersTrace {
- // During testing the launcher is always in portrait mode
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- appLayerReplacesWallpaperLayer(testApp.`package`)
-
- if (!isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- }
- }
-
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
- }
- }
-
- flaky {
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry()
-
- if (isRotated) {
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation)
- }
- }
- }
- }
- }
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 7bfdd96..3cc509f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,28 +17,27 @@
package com.android.server.wm.flicker.rotation
import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+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.endRotation
import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.buildTestTag
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.startRotation
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
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.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -49,78 +48,95 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ChangeAppRotationTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : RotationTransition(InstrumentationRegistry.getInstrumentation()) {
- override val testApp: StandardAppHelper
- get() = SimpleAppHelper(instrumentation)
+ testSpec: FlickerTestParameter
+) : RotationTransition(testSpec) {
+ override val testApp = SimpleAppHelper(instrumentation)
+ override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = emptyMap()
- override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = emptyMap()
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ testSpec.config.endRotation, allStates = false)
+
+ @Presubmit
+ @Test
+ fun screenshotLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.showsLayer(testApp.getPackage())
+ .then()
+ .showsLayer(SCREENSHOT_LAYER)
+ .then()
+ .showsLayer(testApp.getPackage())
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun appLayerRotates_StartingPos() {
+ testSpec.assertLayersStart {
+ this.hasVisibleRegion(testApp.getPackage(), startingPos)
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun appLayerRotates_EndingPos() {
+ testSpec.assertLayersEnd {
+ this.hasVisibleRegion(testApp.getPackage(), endingPos)
+ }
+ }
+
+ @FlakyTest(bugId = 151179149)
+ @Test
+ fun focusDoesNotChange() = testSpec.focusDoesNotChange()
+
+ companion object {
private const val SCREENSHOT_LAYER = "RotationLayer"
- @Parameterized.Parameters(name = "{0}1}")
+ @Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName { buildTestTag(configuration) }
- assertions {
- presubmit {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
-
- layersTrace {
- noUncoveredRegions(configuration.startRotation,
- configuration.endRotation, allStates = false)
-
- all("screenshotLayerBecomesInvisible") {
- this.showsLayer(testApp.getPackage())
- .then()
- .showsLayer(SCREENSHOT_LAYER)
- .then()
- .showsLayer(testApp.getPackage())
- }
- }
- }
-
- flaky {
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- navBarLayerRotatesAndScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
- statusBarLayerRotatesScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
- visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415)
-
- val startingPos = WindowUtils.getDisplayBounds(
- configuration.startRotation)
- val endingPos = WindowUtils.getDisplayBounds(
- configuration.endRotation)
-
- start("appLayerRotates_StartingPos", bugId = 140855415) {
- this.hasVisibleRegion(testApp.getPackage(), startingPos)
- }
-
- end("appLayerRotates_EndingPos", bugId = 140855415) {
- this.hasVisibleRegion(testApp.getPackage(), endingPos)
- }
- }
-
- eventLog {
- focusDoesNotChange(bugId = 151179149)
- }
- }
- }
- }
-
- return FlickerTestRunnerFactory.getInstance()
- .buildRotationTest(instrumentation, transition, testSpec, repetitions = 5)
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigRotationTests(repetitions = 5)
}
}
}
\ No newline at end of file
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 b871e94..04ab84d 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
@@ -18,29 +18,39 @@
import android.app.Instrumentation
import android.os.Bundle
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
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.repetitions
import com.android.server.wm.flicker.startRotation
-abstract class RotationTransition(protected val instrumentation: Instrumentation) {
- abstract val testApp: StandardAppHelper
- abstract fun getAppLaunchParams(configuration: Bundle): Map<String, String>
+abstract class RotationTransition(protected val testSpec: FlickerTestParameter) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val startingPos get() = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
- protected open val transition: FlickerBuilder.(Bundle) -> Unit
- get() = { configuration ->
- repeat { configuration.repetitions }
+ protected abstract val testApp: StandardAppHelper
+ protected abstract fun getAppLaunchParams(configuration: Bundle): Map<String, String>
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
setup {
test {
device.wakeUpAndGoToHomeScreen()
- val extras = getAppLaunchParams(configuration)
+ val extras = getAppLaunchParams(testSpec.config)
testApp.launchViaIntent(wmHelper, stringExtras = extras)
}
eachRun {
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.config.startRotation)
}
}
teardown {
@@ -49,7 +59,8 @@
}
}
transitions {
- this.setRotation(configuration.endRotation)
+ this.setRotation(testSpec.config.endRotation)
}
}
+ }
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 7861464..ef1aead 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,31 +17,30 @@
package com.android.server.wm.flicker.rotation
import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.appWindowAlwaysVisibleOnTop
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.focusDoesNotChange
-import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
-import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.layerAlwaysVisible
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.buildTestTag
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.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.testapp.ActivityOptions
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -52,116 +51,108 @@
*/
@RequiresDevice
@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class SeamlessAppRotationTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object : RotationTransition(InstrumentationRegistry.getInstrumentation()) {
- override val testApp: StandardAppHelper
- get() = SeamlessRotationAppHelper(instrumentation)
+ testSpec: FlickerTestParameter
+) : RotationTransition(testSpec) {
+ override val testApp = SeamlessRotationAppHelper(instrumentation)
- override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = mapOf(
- ActivityOptions.EXTRA_STARVE_UI_THREAD to configuration.starveUiThread.toString()
- )
+ override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = mapOf(
+ ActivityOptions.EXTRA_STARVE_UI_THREAD to configuration.starveUiThread.toString()
+ )
- private val testFactory = FlickerTestRunnerFactory.getInstance()
+ @Presubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
+
+ @Presubmit
+ @Test
+ fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`)
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @FlakyTest(bugId = 147659548)
+ @Test
+ fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ testSpec.config.endRotation, allStates = false)
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
+
+ @FlakyTest
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 147659548)
+ @Test
+ fun appLayerRotates() {
+ testSpec.assertLayers {
+ this.hasVisibleRegion(testApp.`package`, startingPos)
+ }
+ }
+
+ @FlakyTest(bugId = 151179149)
+ @Test
+ fun focusDoesNotChange() = testSpec.focusDoesNotChange()
+
+ companion object {
+ private val testFactory = FlickerTestParameterFactory.getInstance()
private val Bundle.starveUiThread
get() = this.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, false)
- private fun Bundle.createConfig(starveUiThread: Boolean): Bundle {
- val config = this.deepCopy()
+ private fun FlickerTestParameter.createConfig(starveUiThread: Boolean): Bundle {
+ val config = this.config.deepCopy()
config.putBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, starveUiThread)
return config
}
@JvmStatic
- private fun getConfigurations(): List<Bundle> {
- return testFactory.getConfigRotationTests().flatMap {
+ private fun getConfigurations(): List<FlickerTestParameter> {
+ return testFactory.getConfigRotationTests(repetitions = 2).flatMap {
val defaultRun = it.createConfig(starveUiThread = false)
val busyUiRun = it.createConfig(starveUiThread = true)
- listOf(defaultRun, busyUiRun)
+ listOf(
+ FlickerTestParameter(defaultRun),
+ FlickerTestParameter(busyUiRun,
+ name = "${FlickerTestParameter.defaultName(busyUiRun)}_BUSY_UI_THREAD"
+ )
+ )
}
}
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val configurations = getConfigurations()
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName {
- val extra = if (configuration.starveUiThread) {
- "BUSY_UI_THREAD"
- } else {
- ""
- }
- buildTestTag(configuration, extraInfo = extra)
- }
- assertions {
- val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
- val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation)
-
- presubmit {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- appWindowAlwaysVisibleOnTop(testApp.`package`)
- }
-
- layersTrace {
- layerAlwaysVisible(testApp.`package`)
- }
- }
-
- flaky {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible(bugId = 140855415)
- statusBarWindowIsAlwaysVisible(bugId = 140855415)
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(configuration.startRotation,
- configuration.endRotation, allStates = false, bugId = 147659548)
- navBarLayerRotatesAndScales(configuration.startRotation,
- configuration.endRotation)
- statusBarLayerRotatesScales(configuration.startRotation,
- configuration.endRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry()
-
- all("appLayerRotates", bugId = 147659548) {
- if (startingBounds == endingBounds) {
- this.hasVisibleRegion(
- testApp.`package`, startingBounds)
- } else {
- this.hasVisibleRegion(testApp.`package`,
- startingBounds)
- .then()
- .hasVisibleRegion(testApp.`package`,
- endingBounds)
- }
- }
-
- all("noUncoveredRegions", bugId = 147659548) {
- if (startingBounds == endingBounds) {
- this.coversAtLeastRegion(startingBounds)
- } else {
- this.coversAtLeastRegion(startingBounds)
- .then()
- .coversAtLeastRegion(endingBounds)
- }
- }
- }
-
- eventLog {
- focusDoesNotChange(bugId = 151179149)
- }
- }
- }
- }
-
- return testFactory.buildRotationTest(instrumentation, transition, testSpec,
- deviceConfigurations = configurations, repetitions = 2)
+ fun getParams(): Collection<FlickerTestParameter> {
+ return getConfigurations()
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c6c67fe..62ccb1a0 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -400,6 +400,15 @@
</intent-filter>
</activity>
+ <activity android:name="StretchySurfaceViewActivity"
+ android:label="SurfaceView/Stretchy Movement"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="GetBitmapSurfaceViewActivity"
android:label="SurfaceView/GetBitmap with Camera source"
android:exported="true">
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
index 818d899..65d7363 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
@@ -19,8 +19,13 @@
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.PointF;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.RenderNode;
import android.os.Bundle;
+import android.view.MotionEvent;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
@@ -38,7 +43,46 @@
ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge);
layout.addView(spinner);
- ScrollView scrollingThing = new ScrollView(this);
+ ScrollView scrollingThing = new ScrollView(this) {
+ int setting = 0;
+ PointF opts[] = new PointF[] {
+ new PointF(0, 0),
+ new PointF(0, -1f),
+ new PointF(1f, 0),
+ new PointF(0, 1f),
+ new PointF(-1f, 0),
+ new PointF(-1f, 1f),
+ };
+ {
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+ setting = (setting + 1) % opts.length;
+ invalidate();
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ RenderNode node = ((RecordingCanvas) canvas).mNode;
+ PointF dir = opts[setting];
+ float maxStretchAmount = 100f;
+ // Although we could do this in a single call, the real one won't be - so mimic that
+ if (dir.x != 0f) {
+ node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(),
+ dir.x, 0f, maxStretchAmount);
+ }
+ if (dir.y != 0f) {
+ node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(),
+ 0f, dir.y, maxStretchAmount);
+ }
+ }
+ };
scrollingThing.addView(new MyPositionReporter(this));
layout.addView(scrollingThing);
@@ -49,6 +93,11 @@
RenderNode mNode;
int mCurrentCount = 0;
int mTranslateY = 0;
+ Rect mPosition = new Rect();
+ RectF mStretchArea = new RectF();
+ float mStretchX = 0.0f;
+ float mStretchY = 0.0f;
+ float mStretchMax = 0.0f;
MyPositionReporter(Context c) {
super(c);
@@ -78,18 +127,36 @@
canvas.drawRenderNode(mNode);
}
+ void updateText() {
+ setText(String.format("%d: Position %s, stretch area %s, vec %f,%f, amount %f",
+ mCurrentCount, mPosition.toShortString(), mStretchArea.toShortString(),
+ mStretchX, mStretchY, mStretchMax));
+ }
+
@Override
public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
- post(() -> {
+ getHandler().postAtFrontOfQueue(() -> {
mCurrentCount++;
- setText(String.format("%d: Position [%d, %d, %d, %d]", mCurrentCount,
- left, top, right, bottom));
+ mPosition.set(left, top, right, bottom);
+ updateText();
+ });
+ }
+
+ @Override
+ public void applyStretch(long frameNumber, float left, float top, float right, float bottom,
+ float vecX, float vecY, float maxStretch) {
+ getHandler().postAtFrontOfQueue(() -> {
+ mStretchArea.set(left, top, right, bottom);
+ mStretchX = vecX;
+ mStretchY = vecY;
+ mStretchMax = maxStretch;
+ updateText();
});
}
@Override
public void positionLost(long frameNumber) {
- post(() -> {
+ getHandler().postAtFrontOfQueue(() -> {
mCurrentCount++;
setText(mCurrentCount + " No position");
});
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java
new file mode 100644
index 0000000..d604244
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java
@@ -0,0 +1,157 @@
+/*
+ * 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.test.hwui;
+
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.animation.LinearInterpolator;
+import android.widget.FrameLayout;
+
+public class StretchySurfaceViewActivity extends Activity implements Callback {
+ SurfaceView mSurfaceView;
+ ObjectAnimator mAnimator;
+
+ class MySurfaceView extends SurfaceView {
+ boolean mSlow;
+ boolean mScaled;
+ int mToggle = 0;
+
+ public MySurfaceView(Context context) {
+ super(context);
+ setOnClickListener(v -> {
+ mToggle = (mToggle + 1) % 4;
+ mSlow = (mToggle & 0x2) != 0;
+ mScaled = (mToggle & 0x1) != 0;
+
+ mSurfaceView.setScaleX(mScaled ? 1.6f : 1f);
+ mSurfaceView.setScaleY(mScaled ? 0.8f : 1f);
+
+ setTitle("Slow=" + mSlow + ", scaled=" + mScaled);
+ invalidate();
+ });
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ if (mSlow) {
+ try {
+ Thread.sleep(16);
+ } catch (InterruptedException e) {}
+ }
+ }
+
+ public void setMyTranslationY(float ty) {
+ setTranslationY(ty);
+ if (mSlow) {
+ invalidate();
+ }
+ }
+
+ public float getMyTranslationY() {
+ return getTranslationY();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout content = new FrameLayout(this) {
+ {
+ setWillNotDraw(false);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setColor(Color.RED);
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeWidth(10f);
+ canvas.drawLine(0f, 0f, getWidth(), getHeight(), paint);
+ super.onDraw(canvas);
+
+ RenderNode node = ((RecordingCanvas) canvas).mNode;
+ node.stretch(0f, 0f, getWidth(), getHeight() / 2f, 0f, 1f, 400f);
+ }
+ };
+
+ mSurfaceView = new MySurfaceView(this);
+ mSurfaceView.getHolder().addCallback(this);
+
+ final float density = getResources().getDisplayMetrics().density;
+ int size = (int) (200 * density);
+
+ content.addView(mSurfaceView, new FrameLayout.LayoutParams(
+ size, size, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
+ mAnimator = ObjectAnimator.ofFloat(mSurfaceView, "myTranslationY",
+ 0, size);
+ mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
+ mAnimator.setRepeatCount(ObjectAnimator.INFINITE);
+ mAnimator.setDuration(1000);
+ mAnimator.setInterpolator(new LinearInterpolator());
+ setContentView(content);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(Color.WHITE);
+
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setColor(Color.GREEN);
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeWidth(10f);
+ canvas.drawLine(0, 0, width, height, paint);
+
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mAnimator.start();
+ }
+
+ @Override
+ protected void onPause() {
+ mAnimator.pause();
+ super.onPause();
+ }
+}
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index a72b07c..335c8d0 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "InputTests",
srcs: ["src/**/*.kt"],
diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
index f919a3e..c19e5cc 100644
--- a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
+++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
@@ -62,15 +62,12 @@
fun testUpdateFrameInfoFromViewFrameInfo() {
val frameInfo = FrameInfo()
// By default, all values should be zero
- assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(0)
- assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(0)
+ // TODO(b/169866723): Use InputEventAssigner and assert INPUT_EVENT_ID
assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(0)
assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isEqualTo(0)
// The values inside FrameInfo should match those from ViewFrameInfo after we update them
mViewFrameInfo.populateFrameInfo(frameInfo)
- assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(10)
- assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(20)
assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(
FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted)
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 95044dc..6e1cef4 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -105,6 +105,7 @@
key: "com.android.apex.apkrollback.test.key",
apps: ["TestAppAv1"],
installable: false,
+ updatable: false,
}
apex {
@@ -115,6 +116,7 @@
key: "com.android.apex.apkrollback.test.key",
apps: ["TestAppAv2"],
installable: false,
+ updatable: false,
}
apex {
@@ -125,4 +127,5 @@
key: "com.android.apex.apkrollback.test.key",
apps: ["TestAppACrashingV2"],
installable: false,
+ updatable: false,
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 0508125..0bb0337 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -35,7 +35,6 @@
import com.android.cts.install.lib.Install;
import com.android.cts.install.lib.InstallUtils;
import com.android.cts.install.lib.TestApp;
-import com.android.cts.install.lib.Uninstall;
import com.android.cts.rollback.lib.Rollback;
import com.android.cts.rollback.lib.RollbackUtils;
import com.android.internal.R;
@@ -89,7 +88,6 @@
*/
@Test
public void testBadApkOnly_Phase1_Install() throws Exception {
- Uninstall.packages(TestApp.A);
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
Install.single(TestApp.A1).commit();
@@ -149,7 +147,6 @@
*/
@Test
public void testNativeWatchdogTriggersRollback_Phase1_Install() throws Exception {
- Uninstall.packages(TestApp.A);
Install.single(TestApp.A1).commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -183,7 +180,6 @@
*/
@Test
public void testNativeWatchdogTriggersRollbackForAll_Phase1_InstallA() throws Exception {
- Uninstall.packages(TestApp.A);
Install.single(TestApp.A1).commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -201,7 +197,6 @@
TestApp.A)).isNotNull();
// Install another package with rollback
- Uninstall.packages(TestApp.B);
Install.single(TestApp.B1).commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
@@ -238,7 +233,6 @@
@Test
public void testPreviouslyAbandonedRollbacks_Phase1_InstallAndAbandon() throws Exception {
- Uninstall.packages(TestApp.A);
Install.single(TestApp.A1).commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -265,7 +259,6 @@
public void testPreviouslyAbandonedRollbacks_Phase3_VerifyRollback() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
InstallUtils.processUserData(TestApp.A);
- Uninstall.packages(TestApp.A);
}
private static String getModuleMetadataPackageName() {
@@ -301,7 +294,6 @@
@Test
public void testRollbackDataPolicy_Phase1_Install() throws Exception {
- Uninstall.packages(TestApp.A, TestApp.B, TestApp.C);
Install.multi(TestApp.A1, TestApp.B1, TestApp.C1).commit();
// Write user data version = 1
InstallUtils.processUserData(TestApp.A);
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 65fb7b6c..304567a3 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -99,10 +99,16 @@
"/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
runPhase("expireRollbacks");
mLogger.start(getDevice());
+ getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
+ getDevice().uninstallPackage("com.android.cts.install.lib.testapp.B");
+ getDevice().uninstallPackage("com.android.cts.install.lib.testapp.C");
}
@After
public void tearDown() throws Exception {
+ getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
+ getDevice().uninstallPackage("com.android.cts.install.lib.testapp.B");
+ getDevice().uninstallPackage("com.android.cts.install.lib.testapp.C");
mLogger.stop();
runPhase("expireRollbacks");
deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
@@ -283,7 +289,6 @@
*/
@Test
public void testRollbackApexWithApk() throws Exception {
- getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
pushTestApex();
runPhase("testRollbackApexWithApk_Phase1_Install");
getDevice().reboot();
@@ -297,7 +302,6 @@
*/
@Test
public void testRollbackApexWithApkCrashing() throws Exception {
- getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
pushTestApex();
// Install an apex with apk that crashes
diff --git a/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp
index 92e3efa..088d9a2 100644
--- a/tests/SilkFX/Android.bp
+++ b/tests/SilkFX/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SilkFX",
srcs: ["**/*.java", "**/*.kt"],
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
index 48031de..dc75f00 100644
--- a/tests/SurfaceViewBufferTests/Android.bp
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SurfaceViewBufferTests",
srcs: ["**/*.java","**/*.kt"],
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index 43a5078..ee24d48 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "UpdatableSystemFontTest",
srcs: ["src/**/*.java"],
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 1296699..f744d5d 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "UpdatableSystemFontTestKeyPem",
srcs: ["UpdatableSystemFontTestKey.pem"],
diff --git a/tests/benchmarks/internal/Android.bp b/tests/benchmarks/internal/Android.bp
index 9c34eaf..74ed7a3 100644
--- a/tests/benchmarks/internal/Android.bp
+++ b/tests/benchmarks/internal/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "InternalBenchTests",
srcs: ["src/**/*.java"],
@@ -23,4 +32,3 @@
platform_apis: true,
certificate: "platform"
}
-
diff --git a/tests/net/common/java/android/net/NetworkStackTest.java b/tests/net/common/java/android/net/NetworkStackTest.java
index a99aa01..f8f9c72 100644
--- a/tests/net/common/java/android/net/NetworkStackTest.java
+++ b/tests/net/common/java/android/net/NetworkStackTest.java
@@ -15,20 +15,8 @@
*/
package android.net;
-import static android.Manifest.permission.NETWORK_STACK;
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.NetworkStack.checkNetworkStackPermission;
-import static android.net.NetworkStack.checkNetworkStackPermissionOr;
-
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.when;
-import android.content.Context;
import android.os.Build;
import android.os.IBinder;
@@ -46,44 +34,15 @@
@RunWith(AndroidJUnit4.class)
public class NetworkStackTest {
- private static final String [] OTHER_PERMISSION = {"otherpermission1", "otherpermission2"};
-
@Rule
public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
- @Mock Context mCtx;
@Mock private IBinder mConnectorBinder;
@Before public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
- @Test
- public void testCheckNetworkStackPermission() throws Exception {
- when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_GRANTED);
- when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK)))
- .thenReturn(PERMISSION_DENIED);
- checkNetworkStackPermission(mCtx);
- checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION);
-
- when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_DENIED);
- when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK)))
- .thenReturn(PERMISSION_GRANTED);
- checkNetworkStackPermission(mCtx);
- checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION);
-
- when(mCtx.checkCallingOrSelfPermission(any())).thenReturn(PERMISSION_DENIED);
-
- try {
- checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION);
- } catch (SecurityException e) {
- // Expect to get a SecurityException
- return;
- }
-
- fail("Expect fail but permission granted.");
- }
-
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testGetService() {
NetworkStack.setServiceForTest(mConnectorBinder);
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 083c8c8..9ed55f0 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -38,6 +38,7 @@
import android.os.ConditionVariable
import android.os.IBinder
import android.os.INetworkManagementService
+import android.os.SystemConfigManager
import android.os.UserHandle
import android.testing.TestableContext
import android.util.Log
@@ -57,6 +58,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.AdditionalAnswers
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
@@ -94,6 +96,8 @@
private lateinit var netd: INetd
@Mock
private lateinit var dnsResolver: IDnsResolver
+ @Mock
+ private lateinit var systemConfigManager: SystemConfigManager
@Spy
private var context = TestableContext(realContext)
@@ -151,6 +155,11 @@
doReturn(UserHandle.ALL).`when`(asUserCtx).user
doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt())
doNothing().`when`(context).sendStickyBroadcast(any(), any())
+ doReturn(Context.SYSTEM_CONFIG_SERVICE).`when`(context)
+ .getSystemServiceName(SystemConfigManager::class.java)
+ doReturn(systemConfigManager).`when`(context)
+ .getSystemService(Context.SYSTEM_CONFIG_SERVICE)
+ doReturn(IntArray(0)).`when`(systemConfigManager).getSystemPermissionUids(anyString())
networkStackClient = TestNetworkStackClient(realContext)
networkStackClient.init()
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 24e5592..6de1075 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -53,6 +53,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
import static android.net.NetworkCapabilities.NET_CAPABILITY_IA;
@@ -238,6 +239,7 @@
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
+import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -430,6 +432,7 @@
@Mock EthernetManager mEthernetManager;
@Mock NetworkPolicyManager mNetworkPolicyManager;
@Mock KeyStore mKeyStore;
+ @Mock SystemConfigManager mSystemConfigManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -526,6 +529,7 @@
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager;
if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager;
+ if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager;
return super.getSystemService(name);
}
@@ -1432,6 +1436,7 @@
applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
.thenReturn(applicationInfo);
+ when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
@@ -2783,7 +2788,8 @@
if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN ||
capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA ||
capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS ||
- capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP) {
+ capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP
+ || capability == NET_CAPABILITY_ENTERPRISE) {
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
} else {
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
@@ -2882,6 +2888,7 @@
tryNetworkFactoryRequests(NET_CAPABILITY_IA);
tryNetworkFactoryRequests(NET_CAPABILITY_RCS);
tryNetworkFactoryRequests(NET_CAPABILITY_XCAP);
+ tryNetworkFactoryRequests(NET_CAPABILITY_ENTERPRISE);
tryNetworkFactoryRequests(NET_CAPABILITY_EIMS);
tryNetworkFactoryRequests(NET_CAPABILITY_NOT_METERED);
tryNetworkFactoryRequests(NET_CAPABILITY_INTERNET);
@@ -6847,7 +6854,7 @@
callback.expectCapabilitiesThat(mMockVpn, (caps)
-> caps.getUids().size() == 2
&& caps.getUids().contains(new UidRange(uid, uid))
- && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
+ && caps.getUids().contains(createUidRange(RESTRICTED_USER))
&& caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_WIFI));
@@ -6857,7 +6864,7 @@
callback.expectCapabilitiesThat(mMockVpn, (caps)
-> caps.getUids().size() == 2
&& caps.getUids().contains(new UidRange(uid, uid))
- && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
+ && caps.getUids().contains(createUidRange(RESTRICTED_USER))
&& caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_WIFI));
@@ -7491,7 +7498,7 @@
assertNotNull(underlying);
mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY);
// The legacy lockdown VPN only supports userId 0.
- final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ final Set<UidRange> ranges = Collections.singleton(createUidRange(PRIMARY_USER));
mMockVpn.registerAgent(ranges);
mMockVpn.setUnderlyingNetworks(new Network[]{underlying});
mMockVpn.connect(true);
@@ -8410,7 +8417,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
@@ -8438,7 +8445,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
@@ -8454,7 +8461,7 @@
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
@@ -8469,7 +8476,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
@@ -8521,7 +8528,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
- final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
+ final UidRange vpnRange = createUidRange(PRIMARY_USER);
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -8720,7 +8727,7 @@
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
mMockVpn.setVpnType(vpnType);
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
@@ -9279,7 +9286,7 @@
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
- final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
+ final UidRange vpnRange = createUidRange(PRIMARY_USER);
Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -10338,4 +10345,8 @@
mEthernetNetworkAgent.getNetwork().netId, 0 /* times */,
true /* shouldDestroyNetwork */);
}
+
+ private UidRange createUidRange(int userId) {
+ return UidRange.createForUser(UserHandle.of(userId));
+ }
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index c86224a..32c95f1 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -16,12 +16,16 @@
package com.android.server;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.INetd.IF_STATE_DOWN;
+import static android.net.INetd.IF_STATE_UP;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -36,6 +40,7 @@
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.InetAddresses;
+import android.net.InterfaceConfigurationParcel;
import android.net.IpSecAlgorithm;
import android.net.IpSecConfig;
import android.net.IpSecManager;
@@ -48,7 +53,6 @@
import android.net.LinkProperties;
import android.net.Network;
import android.os.Binder;
-import android.os.INetworkManagementService;
import android.os.ParcelFileDescriptor;
import android.system.Os;
import android.test.mock.MockContext;
@@ -148,10 +152,17 @@
}
throw new SecurityException("Unavailable permission requested");
}
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ if (android.Manifest.permission.NETWORK_STACK.equals(permission)) {
+ return PERMISSION_GRANTED;
+ }
+ throw new UnsupportedOperationException();
+ }
};
INetd mMockNetd;
- INetworkManagementService mNetworkManager;
PackageManager mMockPkgMgr;
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
IpSecService mIpSecService;
@@ -181,10 +192,9 @@
@Before
public void setUp() throws Exception {
mMockNetd = mock(INetd.class);
- mNetworkManager = mock(INetworkManagementService.class);
mMockPkgMgr = mock(PackageManager.class);
mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mMockContext, mNetworkManager, mMockIpSecSrvConfig);
+ mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
// Injecting mock netd
when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
@@ -644,7 +654,10 @@
}
private IpSecTunnelInterfaceResponse createAndValidateTunnel(
- String localAddr, String remoteAddr, String pkgName) {
+ String localAddr, String remoteAddr, String pkgName) throws Exception {
+ final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel();
+ config.flags = new String[] {IF_STATE_DOWN};
+ when(mMockNetd.interfaceGetCfg(anyString())).thenReturn(config);
IpSecTunnelInterfaceResponse createTunnelResp =
mIpSecService.createTunnelInterface(
mSourceAddr, mDestinationAddr, fakeNetwork, new Binder(), pkgName);
@@ -674,7 +687,8 @@
anyInt(),
anyInt(),
anyInt());
- verify(mNetworkManager).setInterfaceUp(createTunnelResp.interfaceName);
+ verify(mMockNetd).interfaceSetCfg(argThat(
+ config -> Arrays.asList(config.flags).contains(IF_STATE_UP)));
}
@Test
diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
index 788e4ef..22a2c94 100644
--- a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
@@ -31,7 +31,6 @@
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
@@ -62,8 +61,7 @@
public void setUp() throws Exception {
mMockContext = mock(Context.class);
mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(
- mMockContext, mock(INetworkManagementService.class), mMockIpSecSrvConfig);
+ mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
}
private void assertResourceState(
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 536e983..f97eabf 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -42,7 +42,6 @@
import android.net.IpSecSpiResponse;
import android.net.IpSecUdpEncapResponse;
import android.os.Binder;
-import android.os.INetworkManagementService;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.system.ErrnoException;
@@ -116,7 +115,6 @@
}
Context mMockContext;
- INetworkManagementService mMockNetworkManager;
INetd mMockNetd;
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
IpSecService mIpSecService;
@@ -124,10 +122,9 @@
@Before
public void setUp() throws Exception {
mMockContext = mock(Context.class);
- mMockNetworkManager = mock(INetworkManagementService.class);
mMockNetd = mock(INetd.class);
mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mMockContext, mMockNetworkManager, mMockIpSecSrvConfig);
+ mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
// Injecting mock netd
when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
@@ -135,7 +132,7 @@
@Test
public void testIpSecServiceCreate() throws InterruptedException {
- IpSecService ipSecSrv = IpSecService.create(mMockContext, mMockNetworkManager);
+ IpSecService ipSecSrv = IpSecService.create(mMockContext);
assertNotNull(ipSecSrv);
}
@@ -608,7 +605,7 @@
public void testOpenUdpEncapSocketTagsSocket() throws Exception {
IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
IpSecService testIpSecService = new IpSecService(
- mMockContext, mMockNetworkManager, mMockIpSecSrvConfig, mockTagger);
+ mMockContext, mMockIpSecSrvConfig, mockTagger);
IpSecUdpEncapResponse udpEncapResp =
testIpSecService.openUdpEncapsulationSocket(0, new Binder());
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 8f5ae97..e4e24b4 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -61,6 +61,7 @@
import android.net.INetd;
import android.net.UidRange;
import android.os.Build;
+import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.SparseIntArray;
@@ -114,6 +115,7 @@
@Mock private PackageManagerInternal mMockPmi;
@Mock private UserManager mUserManager;
@Mock private PermissionMonitor.Dependencies mDeps;
+ @Mock private SystemConfigManager mSystemConfigManager;
private PermissionMonitor mPermissionMonitor;
@@ -124,6 +126,11 @@
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mUserManager.getUserHandles(eq(true))).thenReturn(
Arrays.asList(new UserHandle[] { MOCK_USER1, MOCK_USER2 }));
+ when(mContext.getSystemServiceName(SystemConfigManager.class))
+ .thenReturn(Context.SYSTEM_CONFIG_SERVICE);
+ when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE))
+ .thenReturn(mSystemConfigManager);
+ when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
@@ -747,4 +754,20 @@
GET_PERMISSIONS | MATCH_ANY_USER);
assertTrue(monitor.hasPermission(systemInfo, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
}
+
+ @Test
+ public void testUpdateUidPermissionsFromSystemConfig() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+ when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(new ArrayList<>());
+ when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET)))
+ .thenReturn(new int[]{ MOCK_UID1, MOCK_UID2 });
+ when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS)))
+ .thenReturn(new int[]{ MOCK_UID2 });
+
+ mPermissionMonitor.startMonitoring();
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{ MOCK_UID1 });
+ mNetdServiceMonitor.expectPermission(
+ INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ new int[]{ MOCK_UID2 });
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index cffd2d1d..7489a0f 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -21,6 +21,8 @@
import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static android.content.pm.UserInfo.FLAG_RESTRICTED;
import static android.net.ConnectivityManager.NetworkCallback;
+import static android.net.INetd.IF_STATE_DOWN;
+import static android.net.INetd.IF_STATE_UP;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -62,6 +64,7 @@
import android.net.INetd;
import android.net.Ikev2VpnProfile;
import android.net.InetAddresses;
+import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
import android.net.IpSecManager;
import android.net.IpSecTunnelInterfaceResponse;
@@ -179,7 +182,8 @@
mPackages.put(PKGS[i], PKG_UIDS[i]);
}
}
- private static final UidRange PRI_USER_RANGE = UidRange.createForUser(primaryUser.id);
+ private static final UidRange PRI_USER_RANGE =
+ UidRange.createForUser(UserHandle.of(primaryUser.id));
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
@Mock private UserManager mUserManager;
@@ -269,7 +273,7 @@
vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
- PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
+ PRI_USER_RANGE, UidRange.createForUser(UserHandle.of(restrictedProfileA.id))
})), ranges);
}
@@ -872,17 +876,28 @@
eq(AppOpsManager.MODE_IGNORED));
}
- private NetworkCallback triggerOnAvailableAndGetCallback() {
+ private NetworkCallback triggerOnAvailableAndGetCallback() throws Exception {
final ArgumentCaptor<NetworkCallback> networkCallbackCaptor =
ArgumentCaptor.forClass(NetworkCallback.class);
verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
.requestNetwork(any(), networkCallbackCaptor.capture());
+ // onAvailable() will trigger onDefaultNetworkChanged(), so NetdUtils#setInterfaceUp will be
+ // invoked. Set the return value of INetd#interfaceGetCfg to prevent NullPointerException.
+ final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel();
+ config.flags = new String[] {IF_STATE_DOWN};
+ when(mNetd.interfaceGetCfg(anyString())).thenReturn(config);
final NetworkCallback cb = networkCallbackCaptor.getValue();
cb.onAvailable(TEST_NETWORK);
return cb;
}
+ private void verifyInterfaceSetCfgWithFlags(String flag) throws Exception {
+ // Add a timeout for waiting for interfaceSetCfg to be called.
+ verify(mNetd, timeout(TEST_TIMEOUT_MS)).interfaceSetCfg(argThat(
+ config -> Arrays.asList(config.flags).contains(flag)));
+ }
+
@Test
public void testStartPlatformVpnAuthenticationFailed() throws Exception {
final ArgumentCaptor<IkeSessionCallback> captor =
@@ -894,6 +909,8 @@
final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile));
final NetworkCallback cb = triggerOnAvailableAndGetCallback();
+ verifyInterfaceSetCfgWithFlags(IF_STATE_UP);
+
// Wait for createIkeSession() to be called before proceeding in order to ensure consistent
// state
verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS))
@@ -912,6 +929,8 @@
final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile);
final NetworkCallback cb = triggerOnAvailableAndGetCallback();
+ verifyInterfaceSetCfgWithFlags(IF_STATE_UP);
+
// Wait for createIkeSession() to be called before proceeding in order to ensure consistent
// state
verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 7087676..66590c9 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -16,6 +16,8 @@
package android.net.vcn;
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
+
import static androidx.test.InstrumentationRegistry.getContext;
import static org.junit.Assert.assertEquals;
@@ -204,10 +206,13 @@
cbBinder.onEnteredSafeMode();
verify(mMockStatusCallback).onEnteredSafeMode();
+ cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
+ verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
+
cbBinder.onGatewayConnectionError(
UNDERLYING_NETWORK_CAPABILITIES,
VcnManager.VCN_ERROR_CODE_NETWORK_ERROR,
- "java.net.UnknownHostException",
+ UnknownHostException.class.getName(),
"exception_message");
verify(mMockStatusCallback)
.onGatewayConnectionError(
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
index 3ba0a1f..a674425 100644
--- a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
@@ -46,6 +46,6 @@
@Test
public void testParcelUnparcel() {
- assertParcelSane(SAMPLE_NETWORK_POLICY, 2);
+ assertParcelSane(SAMPLE_NETWORK_POLICY, 1);
}
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 45b2381..9b500a7 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -43,7 +43,6 @@
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -59,6 +58,7 @@
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
+import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
import android.os.IBinder;
@@ -783,7 +783,7 @@
true /* hasPermissionsforSubGroup */,
true /* hasLocationPermission */);
- verify(mMockStatusCallback, times(1)).onEnteredSafeMode();
+ verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
}
@Test
@@ -795,7 +795,8 @@
false /* hasPermissionsforSubGroup */,
true /* hasLocationPermission */);
- verify(mMockStatusCallback, never()).onEnteredSafeMode();
+ verify(mMockStatusCallback, never())
+ .onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
}
@Test
@@ -807,7 +808,8 @@
true /* hasPermissionsforSubGroup */,
false /* hasLocationPermission */);
- verify(mMockStatusCallback, never()).onEnteredSafeMode();
+ verify(mMockStatusCallback, never())
+ .onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
}
@Test
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index f0b7595..ebc0ec1 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -11,6 +11,12 @@
EMOJI_VS = 0xFE0F
+#TODO(179952916): Rename CutiveMono and DancingScript
+CANONICAL_NAME_EXCEPTION_LIST = [
+ 'CutiveMono.ttf',
+ 'DancingScript-Regular.ttf',
+]
+
LANG_TO_SCRIPT = {
'as': 'Beng',
'be': 'Cyrl',
@@ -665,6 +671,53 @@
assert_font_supports_none_of_chars(record.font, cjk_punctuation, name)
+def getPostScriptName(font):
+ ttf = open_font(font)
+ nameTable = ttf['name']
+ for name in nameTable.names:
+ if name.nameID == 6 and name.platformID == 3 and name.platEncID == 1 and name.langID == 0x0409:
+ return str(name)
+
+
+def getSuffix(font):
+ file_path, index = font
+ with open(path.join(_fonts_dir, file_path), 'rb') as f:
+ tag = f.read(4)
+ isCollection = tag == b'ttcf'
+
+ ttf = open_font(font)
+ isType1 = ('CFF ' in ttf or 'CFF2' in ttf)
+
+ if isType1:
+ if isCollection:
+ return '.otc'
+ else:
+ return '.otf'
+ else:
+ if isCollection:
+ return '.ttc'
+ else:
+ return '.ttf'
+
+
+def check_canonical_name():
+ for record in _all_fonts:
+ file_name, index = record.font
+ if file_name in CANONICAL_NAME_EXCEPTION_LIST:
+ continue
+
+ if index and index != 0:
+ continue
+
+ psName = getPostScriptName(record.font)
+ assert psName, 'PostScript must be defined'
+
+ suffix = getSuffix(record.font)
+ canonicalName = '%s%s' % (psName, suffix)
+
+ assert file_name == canonicalName, (
+ '%s is not a canonical name. Must be %s' % (file_name, canonicalName))
+
def main():
global _fonts_dir
target_out = sys.argv[1]
@@ -682,6 +735,8 @@
check_cjk_punctuation()
+ check_canonical_name()
+
check_emoji = sys.argv[2]
if check_emoji == 'true':
ucd_path = sys.argv[3]
diff --git a/tools/powerstats/Android.bp b/tools/powerstats/Android.bp
index af41144..9c58daf0 100644
--- a/tools/powerstats/Android.bp
+++ b/tools/powerstats/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_binary_host {
name: "PowerStatsServiceProtoParser",
manifest: "PowerStatsServiceProtoParser_manifest.txt",
diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp
index e255f7c..82a5dac 100644
--- a/tools/processors/intdef_mappings/Android.bp
+++ b/tools/processors/intdef_mappings/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_plugin {
name: "intdef-annotation-processor",
@@ -30,4 +39,4 @@
],
test_suites: ["general-tests"],
-}
\ No newline at end of file
+}
diff --git a/tools/xmlpersistence/Android.bp b/tools/xmlpersistence/Android.bp
index d58d0dc..0b6dba6 100644
--- a/tools/xmlpersistence/Android.bp
+++ b/tools/xmlpersistence/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_binary_host {
name: "xmlpersistence_cli",
manifest: "manifest.txt",