Merge "Fix render_ahead properties" into sc-dev
diff --git a/Android.bp b/Android.bp
index d170913..202e548 100644
--- a/Android.bp
+++ b/Android.bp
@@ -632,6 +632,7 @@
],
sdk_version: "core_platform",
static_libs: [
+ "bouncycastle-repackaged-unbundled",
"framework-internal-utils",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
// in favor of an API stubs dependency in java_library "framework" below.
@@ -1294,6 +1295,18 @@
],
}
+python_binary_host {
+ name: "update_font_metadata",
+ defaults: ["base_default"],
+ main: "tools/fonts/update_font_metadata.py",
+ srcs: [
+ "tools/fonts/update_font_metadata.py",
+ ],
+ libs: [
+ "fontTools",
+ ],
+}
+
filegroup {
name: "framework-media-annotation-srcs",
srcs: [
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index cdf5df6c..30ed7de 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,6 +8,7 @@
cmds/input/
cmds/uinput/
core/jni/
+ libs/hwui/
libs/input/
native/
services/core/jni/
diff --git a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
index 9e519f7..1d94d7e 100644
--- a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
@@ -178,7 +178,7 @@
// For testing, just disable enforcement to avoid hooking up to compat framework
ParseTypeImpl(ParseInput.Callback { _, _, _ -> false })
}
- val parser = ParsingPackageUtils(false, null, null,
+ val parser = ParsingPackageUtils(false, null, null, emptyList(),
object : ParsingPackageUtils.Callback {
override fun hasFeature(feature: String) = true
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index c37f6d9..a2dc1c2 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -19,14 +19,12 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
import android.perftests.utils.ManualBenchmarkState;
import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
import android.perftests.utils.PerfManualStatusReporter;
import android.view.Display;
-import android.view.DisplayCutout;
import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InsetsSourceControl;
@@ -85,9 +83,6 @@
private static class TestWindow extends BaseIWindow {
final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
final InsetsState mRequestedVisibility = new InsetsState();
- final Rect mOutFrame = new Rect();
- final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
final InsetsState mOutInsetsState = new InsetsState();
final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
@@ -107,7 +102,7 @@
long startTime = SystemClock.elapsedRealtimeNanos();
session.addToDisplay(this, mLayoutParams, View.VISIBLE,
- Display.DEFAULT_DISPLAY, mRequestedVisibility, mOutFrame, inputChannel,
+ Display.DEFAULT_DISPLAY, mRequestedVisibility, inputChannel,
mOutInsetsState, mOutControls);
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
state.addExtraResult("add", elapsedTimeNsOfAdd);
diff --git a/apex/Android.bp b/apex/Android.bp
index 1876110..8310ba7 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -15,189 +15,3 @@
package {
default_visibility: [":__subpackages__"],
}
-
-mainline_stubs_args =
- "--error UnhiddenSystemApi " +
- "--hide BroadcastBehavior " +
- "--hide CallbackInterface " +
- "--hide DeprecationMismatch " +
- "--hide HiddenSuperclass " +
- "--hide HiddenTypedefConstant " +
- "--hide HiddenTypeParameter " +
- "--hide MissingPermission " +
- "--hide RequiresPermission " +
- "--hide SdkConstant " +
- "--hide Todo " +
- "--hide Typo " +
- "--hide UnavailableSymbol "
-
-// TODO: modularize this so not every module has the same whitelist
-framework_packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
-// TODO: remove the hiding when server classes are cleaned up.
-mainline_framework_stubs_args =
- mainline_stubs_args +
- "--hide-package com.android.server "
-
-priv_apps = " " +
- "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\) "
-
-module_libs = " " +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
- "\\)" +
- " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\) "
-
-mainline_service_stubs_args =
- mainline_stubs_args +
- "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.SYSTEM_SERVER" +
- "\\) " +
- "--hide-annotation android.annotation.Hide " +
- "--hide InternalClasses " // com.android.* classes are okay in this interface
-
-// Defaults common to all mainline module java_sdk_library instances.
-java_defaults {
- name: "framework-module-common-defaults",
-
- // Additional annotations used for compiling both the implementation and the
- // stubs libraries.
- libs: ["framework-annotations-lib"],
-
- // Framework modules are not generally shared libraries, i.e. they are not
- // intended, and must not be allowed, to be used in a <uses-library> manifest
- // entry.
- shared_library: false,
-
- // Prevent dependencies that do not specify an sdk_version from accessing the
- // implementation library by default and force them to use stubs instead.
- default_to_stubs: true,
-
- // Enable api lint. This will eventually become the default for java_sdk_library
- // but it cannot yet be turned on because some usages have not been cleaned up.
- // TODO(b/156126315) - Remove when no longer needed.
- api_lint: {
- enabled: true,
- },
-
- // The API scope specific properties.
- public: {
- enabled: true,
- sdk_version: "module_current",
- },
-
- // installable implies we'll create a non-apex (platform) variant, which
- // we shouldn't ordinarily need (and it can create issues), so disable that.
- installable: false,
-
- // Configure framework module specific metalava options.
- droiddoc_options: [mainline_stubs_args],
-
- annotations_enabled: true,
-
- // Allow access to the stubs from anywhere
- visibility: ["//visibility:public"],
- stubs_library_visibility: ["//visibility:public"],
-
- // Hide impl library and stub sources
- impl_library_visibility: [
- ":__pkg__",
- "//frameworks/base", // For framework-all
- ],
- stubs_source_visibility: ["//visibility:private"],
-
- defaults_visibility: ["//visibility:private"],
-
- // Collates API usages from each module for further analysis.
- plugins: ["java_api_finder"],
-
- // Mainline modules should only rely on 'module_lib' APIs provided by other modules
- // and the non updatable parts of the platform.
- sdk_version: "module_current",
-}
-
-// Defaults for mainline module provided java_sdk_library instances.
-java_defaults {
- name: "framework-module-defaults",
- defaults: ["framework-module-common-defaults"],
-
- system: {
- enabled: true,
- sdk_version: "module_current",
- },
- module_lib: {
- enabled: true,
- sdk_version: "module_current",
- },
- defaults_visibility: [
- ":__subpackages__",
- "//frameworks/base/libs/hwui",
- "//frameworks/base/wifi",
- "//packages/modules:__subpackages__",
- "//packages/providers/MediaProvider:__subpackages__",
- ],
-}
-
-// Defaults for mainline module system server provided java_sdk_library instances.
-java_defaults {
- name: "framework-system-server-module-defaults",
- defaults: ["framework-module-common-defaults"],
-
- system_server: {
- enabled: true,
- sdk_version: "module_current",
- },
- defaults_visibility: [
- ":__subpackages__",
- "//packages/modules:__subpackages__",
- ],
-}
-
-stubs_defaults {
- name: "service-module-stubs-srcs-defaults",
- args: mainline_service_stubs_args,
- installable: false,
- annotations_enabled: true,
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- filter_packages: ["com.android."],
- check_api: {
- current: {
- api_file: "api/current.txt",
- removed_api_file: "api/removed.txt",
- },
- api_lint: {
- enabled: true,
- },
- },
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system-server/api",
- },
-}
-
-// Empty for now, but a convenient place to add rules for all
-// module java_library system_server stub libs.
-java_defaults {
- name: "service-module-stubs-defaults",
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system-server",
- },
-}
diff --git a/apex/OWNERS b/apex/OWNERS
index bde2bec..b3e81b9 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,8 +1 @@
-# Mainline modularization team
-
-andreionea@google.com
-dariofreni@google.com
-hansson@google.com
-mathewi@google.com
-pedroql@google.com
-satayev@google.com
+file:platform/packages/modules/common:/OWNERS
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index b1394c1..a3b8013 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -143,11 +143,11 @@
method public void close();
method public void getByUri(@NonNull android.app.appsearch.GetByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,android.app.appsearch.GenericDocument>);
method public void getSchema(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.Set<android.app.appsearch.AppSearchSchema>>>);
- method public void putDocuments(@NonNull android.app.appsearch.PutDocumentsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
- method @NonNull public android.app.appsearch.SearchResults query(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
- method public void removeByQuery(@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 public void removeByUri(@NonNull android.app.appsearch.RemoveByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
+ 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 @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>>);
}
@@ -216,7 +216,7 @@
public class GlobalSearchSession implements java.io.Closeable {
method public void close();
- method @NonNull public android.app.appsearch.SearchResults query(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
+ method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
}
public class PackageIdentifier {
@@ -226,13 +226,13 @@
}
public final class PutDocumentsRequest {
- method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getDocuments();
+ method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getGenericDocuments();
}
public static final class PutDocumentsRequest.Builder {
ctor public PutDocumentsRequest.Builder();
- method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull android.app.appsearch.GenericDocument...);
- method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull java.util.Collection<? extends android.app.appsearch.GenericDocument>);
+ method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocuments(@NonNull android.app.appsearch.GenericDocument...);
+ method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocuments(@NonNull java.util.Collection<? extends android.app.appsearch.GenericDocument>);
method @NonNull public android.app.appsearch.PutDocumentsRequest build();
}
@@ -290,14 +290,14 @@
}
public final class SearchSpec {
+ method @NonNull public java.util.List<java.lang.String> getFilterNamespaces();
method @NonNull public java.util.List<java.lang.String> getFilterPackageNames();
+ method @NonNull public java.util.List<java.lang.String> getFilterSchemas();
method public int getMaxSnippetSize();
- method @NonNull public java.util.List<java.lang.String> getNamespaces();
method public int getOrder();
method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getProjections();
method public int getRankingStrategy();
method public int getResultCountPerPage();
- method @NonNull public java.util.List<java.lang.String> getSchemaTypes();
method public int getSnippetCount();
method public int getSnippetCountPerProperty();
method public int getTermMatch();
@@ -316,14 +316,14 @@
public static final class SearchSpec.Builder {
ctor public SearchSpec.Builder();
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterNamespaces(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterNamespaces(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.util.Collection<java.lang.String>);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec build();
method @NonNull public android.app.appsearch.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_SIZE_LIMIT) int);
method @NonNull public android.app.appsearch.SearchSpec.Builder setOrder(int);
@@ -344,8 +344,8 @@
public static final class SetSchemaRequest.Builder {
ctor public SetSchemaRequest.Builder();
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull android.app.appsearch.AppSearchSchema...);
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull android.app.appsearch.AppSearchSchema...);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>);
method @NonNull public android.app.appsearch.SetSchemaRequest build();
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean);
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.AppSearchSchema.Migrator);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 814800e..69d4e53 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -270,12 +270,12 @@
* they were successfully indexed, or a failed {@link AppSearchResult} otherwise.
* @throws RuntimeException If an error occurred during the execution.
* @hide
- * @deprecated use {@link AppSearchSession#putDocuments} instead.
+ * @deprecated use {@link AppSearchSession#put} instead.
*/
public AppSearchBatchResult<String, Void> putDocuments(@NonNull PutDocumentsRequest request) {
// TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in
// one big list.
- List<GenericDocument> documents = request.getDocuments();
+ List<GenericDocument> documents = request.getGenericDocuments();
List<Bundle> documentBundles = new ArrayList<>(documents.size());
for (int i = 0; i < documents.size(); i++) {
documentBundles.add(documents.get(i).getBundle());
@@ -330,7 +330,7 @@
DEFAULT_DATABASE_NAME,
request.getNamespace(),
uris,
- request.getProjectionsVisibleToPackagesInternal(),
+ request.getProjectionsInternal(),
mContext.getUserId(),
new IAppSearchBatchResultCallback.Stub() {
public void onResult(AppSearchBatchResult result) {
@@ -465,7 +465,7 @@
* {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}.
* @throws RuntimeException If an error occurred during the execution.
* @hide
- * @deprecated use {@link AppSearchSession#removeByUri} instead.
+ * @deprecated use {@link AppSearchSession#remove} instead.
*/
public AppSearchBatchResult<String, Void> removeByUri(@NonNull RemoveByUriRequest request) {
List<String> uris = new ArrayList<>(request.getUris());
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 670f8b9..8723515 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -102,7 +102,7 @@
}
/**
- * Sets the schema that will be used by documents provided to the {@link #putDocuments} method.
+ * Sets the schema that will be used by documents provided to the {@link #put} method.
*
* <p>The schema provided here is compared to the stored copy of the schema previously supplied
* to {@link #setSchema}, if any, to determine how to treat existing documents. The following
@@ -268,7 +268,7 @@
* {@link Throwable} if an unexpected internal error occurred in AppSearch
* service.
*/
- public void putDocuments(
+ public void put(
@NonNull PutDocumentsRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull BatchResultCallback<String, Void> callback) {
@@ -276,7 +276,7 @@
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
- List<GenericDocument> documents = request.getDocuments();
+ List<GenericDocument> documents = request.getGenericDocuments();
List<Bundle> documentBundles = new ArrayList<>(documents.size());
for (int i = 0; i < documents.size(); i++) {
documentBundles.add(documents.get(i).getBundle());
@@ -327,7 +327,7 @@
mDatabaseName,
request.getNamespace(),
new ArrayList<>(request.getUris()),
- request.getProjectionsVisibleToPackagesInternal(),
+ request.getProjectionsInternal(),
mUserId,
new IAppSearchBatchResultCallback.Stub() {
public void onResult(AppSearchBatchResult result) {
@@ -423,7 +423,7 @@
* @return The search result of performing this operation.
*/
@NonNull
- public SearchResults query(
+ public SearchResults search(
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec,
@NonNull @CallbackExecutor Executor executor) {
@@ -441,7 +441,7 @@
* <p>A usage report represents an event in which a user interacted with or viewed a document.
*
* <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
- * metrics for that particular document. These metrics are used for ordering {@link #query}
+ * metrics for that particular document. These metrics are used for ordering {@link #search}
* results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and
* {@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
*
@@ -494,7 +494,7 @@
* {@link Throwable} if an unexpected internal error occurred in AppSearch
* service.
*/
- public void removeByUri(
+ public void remove(
@NonNull RemoveByUriRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull BatchResultCallback<String, Void> callback) {
@@ -523,7 +523,8 @@
/**
* Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
* match the {@code queryExpression} in given namespaces and schemaTypes which is set via
- * {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}.
+ * {@link SearchSpec.Builder#addFilterNamespaces} and
+ * {@link SearchSpec.Builder#addFilterSchemas}.
*
* <p> An empty {@code queryExpression} matches all documents.
*
@@ -539,7 +540,8 @@
* the operation succeeds, the callback will be invoked with
* {@code null}.
*/
- public void removeByQuery(@NonNull String queryExpression,
+ public void remove(
+ @NonNull String queryExpression,
@NonNull SearchSpec searchSpec,
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<AppSearchResult<Void>> callback) {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index 6bb8554..8651834 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -134,7 +134,7 @@
* @return The search result of performing this operation.
*/
@NonNull
- public SearchResults query(
+ public SearchResults search(
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec,
@NonNull @CallbackExecutor Executor executor) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 2e00ff2..e94b3b2 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -141,10 +141,6 @@
}
/** Adds a property to the given type. */
- // TODO(b/171360120): MissingGetterMatchingBuilder expects a method called getPropertys, but
- // we provide the (correct) method getProperties. Once the bug referenced in this TODO is
- // fixed, remove this SuppressLint.
- @SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
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 2f02808..4c11514 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -22,6 +22,7 @@
import android.annotation.SuppressLint;
import android.app.appsearch.util.BundleUtil;
import android.os.Bundle;
+import android.os.Parcelable;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -37,9 +38,9 @@
*
* <p>Documents are constructed via {@link GenericDocument.Builder}.
*
- * @see AppSearchSession#putDocuments
+ * @see AppSearchSession#put
* @see AppSearchSession#getByUri
- * @see AppSearchSession#query
+ * @see AppSearchSession#search
*/
public class GenericDocument {
private static final String TAG = "AppSearchGenericDocumen";
@@ -210,7 +211,7 @@
Object property = mProperties.get(key);
if (property instanceof ArrayList) {
return getPropertyBytesArray(key);
- } else if (property instanceof Bundle[]) {
+ } else if (property instanceof Parcelable[]) {
return getPropertyDocumentArray(key);
}
return property;
@@ -436,7 +437,7 @@
@Nullable
public GenericDocument[] getPropertyDocumentArray(@NonNull String key) {
Preconditions.checkNotNull(key);
- Bundle[] bundles = getAndCastPropertyArray(key, Bundle[].class);
+ Parcelable[] bundles = getAndCastPropertyArray(key, Parcelable[].class);
if (bundles == null || bundles.length == 0) {
return null;
}
@@ -446,7 +447,18 @@
Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key);
continue;
}
- documents[i] = new GenericDocument(bundles[i]);
+ if (!(bundles[i] instanceof Bundle)) {
+ Log.e(
+ TAG,
+ "The inner element at "
+ + i
+ + " is a "
+ + bundles[i].getClass()
+ + ", not a Bundle for key: "
+ + key);
+ continue;
+ }
+ documents[i] = new GenericDocument((Bundle) bundles[i]);
}
return documents;
}
@@ -574,8 +586,8 @@
* @param schemaType The schema type of the {@link GenericDocument}. The passed-in {@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#putDocuments}. Otherwise, the document will be rejected by {@link
- * AppSearchSession#putDocuments}.
+ * AppSearchSession#put}. Otherwise, the document will be rejected by {@link
+ * AppSearchSession#put}.
*/
@SuppressWarnings("unchecked")
public Builder(@NonNull String uri, @NonNull String schemaType) {
@@ -817,7 +829,7 @@
private void putInPropertyBundle(@NonNull String key, @NonNull GenericDocument[] values) {
validateRepeatedPropertyLength(key, values.length);
- Bundle[] documentBundles = new Bundle[values.length];
+ Parcelable[] documentBundles = new Parcelable[values.length];
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
throw new IllegalArgumentException("The document at " + i + " is null.");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 38e0046..0fcf061 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -96,7 +96,7 @@
* @hide
*/
@NonNull
- public Map<String, List<String>> getProjectionsVisibleToPackagesInternal() {
+ public Map<String, List<String>> getProjectionsInternal() {
return mTypePropertyPathsMap;
}
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 0f141d6..05b2128 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
@@ -16,8 +16,8 @@
package android.app.appsearch;
+
import android.annotation.NonNull;
-import android.annotation.SuppressLint;
import com.android.internal.util.Preconditions;
@@ -41,7 +41,7 @@
/** Returns the documents that are part of this request. */
@NonNull
- public List<GenericDocument> getDocuments() {
+ public List<GenericDocument> getGenericDocuments() {
return Collections.unmodifiableList(mDocuments);
}
@@ -55,17 +55,15 @@
private boolean mBuilt = false;
/** Adds one or more {@link GenericDocument} objects to the request. */
- @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@NonNull
- public Builder addGenericDocument(@NonNull GenericDocument... documents) {
+ public Builder addGenericDocuments(@NonNull GenericDocument... documents) {
Preconditions.checkNotNull(documents);
- return addGenericDocument(Arrays.asList(documents));
+ return addGenericDocuments(Arrays.asList(documents));
}
/** Adds a collection of {@link GenericDocument} objects to the request. */
- @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@NonNull
- public Builder addGenericDocument(
+ public Builder addGenericDocuments(
@NonNull Collection<? extends GenericDocument> documents) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(documents);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index a04da34..2104198d 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -29,7 +29,7 @@
/**
* Encapsulates a request to remove documents by namespace and URI.
*
- * @see AppSearchSession#removeByUri
+ * @see AppSearchSession#remove
*/
public final class RemoveByUriRequest {
private final String mNamespace;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
index 963062c..f72f8e1 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.IllegalSearchSpecException;
import android.os.Bundle;
import android.util.ArrayMap;
@@ -49,7 +48,7 @@
public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
- static final String SCHEMA_TYPE_FIELD = "schemaType";
+ static final String SCHEMA_FIELD = "schema";
static final String NAMESPACE_FIELD = "namespace";
static final String PACKAGE_NAME_FIELD = "packageName";
static final String NUM_PER_PAGE_FIELD = "numPerPage";
@@ -171,12 +170,12 @@
* <p>If empty, the query will search over all schema types.
*/
@NonNull
- public List<String> getSchemaTypes() {
- List<String> schemaTypes = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD);
- if (schemaTypes == null) {
+ public List<String> getFilterSchemas() {
+ List<String> schemas = mBundle.getStringArrayList(SCHEMA_FIELD);
+ if (schemas == null) {
return Collections.emptyList();
}
- return Collections.unmodifiableList(schemaTypes);
+ return Collections.unmodifiableList(schemas);
}
/**
@@ -185,7 +184,7 @@
* <p>If empty, the query will search over all namespaces.
*/
@NonNull
- public List<String> getNamespaces() {
+ public List<String> getFilterNamespaces() {
List<String> namespaces = mBundle.getStringArrayList(NAMESPACE_FIELD);
if (namespaces == null) {
return Collections.emptyList();
@@ -209,24 +208,6 @@
return Collections.unmodifiableList(packageNames);
}
- /**
- * Returns the list of package names to search over.
- *
- * <p>If unset, the query will search over all packages that the caller has access to. If
- * package names are specified which caller doesn't have access to, then those package names
- * will be ignored.
- *
- * @hide
- */
- @NonNull
- public List<String> getPackageNames() {
- List<String> packageNames = mBundle.getStringArrayList(PACKAGE_NAME_FIELD);
- if (packageNames == null) {
- return Collections.emptyList();
- }
- return Collections.unmodifiableList(packageNames);
- }
-
/** Returns the number of results per page in the result set. */
public int getResultCountPerPage() {
return mBundle.getInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
@@ -270,11 +251,10 @@
@NonNull
public Map<String, List<String>> getProjections() {
Bundle typePropertyPathsBundle = mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD);
- Set<String> schemaTypes = typePropertyPathsBundle.keySet();
- Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemaTypes.size());
- for (String schemaType : schemaTypes) {
- typePropertyPathsMap.put(
- schemaType, typePropertyPathsBundle.getStringArrayList(schemaType));
+ Set<String> schemas = typePropertyPathsBundle.keySet();
+ Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemas.size());
+ for (String schema : schemas) {
+ typePropertyPathsMap.put(schema, typePropertyPathsBundle.getStringArrayList(schema));
}
return typePropertyPathsMap;
}
@@ -283,7 +263,7 @@
public static final class Builder {
private final Bundle mBundle;
- private final ArrayList<String> mSchemaTypes = new ArrayList<>();
+ private final ArrayList<String> mSchemas = new ArrayList<>();
private final ArrayList<String> mNamespaces = new ArrayList<>();
private final ArrayList<String> mPackageNames = new ArrayList<>();
private final Bundle mProjectionTypePropertyMasks = new Bundle();
@@ -312,10 +292,10 @@
* <p>If unset, the query will search over all schema types.
*/
@NonNull
- public Builder addSchemaType(@NonNull String... schemaTypes) {
- Preconditions.checkNotNull(schemaTypes);
+ public Builder addFilterSchemas(@NonNull String... schemas) {
+ Preconditions.checkNotNull(schemas);
Preconditions.checkState(!mBuilt, "Builder has already been used");
- return addSchemaType(Arrays.asList(schemaTypes));
+ return addFilterSchemas(Arrays.asList(schemas));
}
/**
@@ -325,10 +305,10 @@
* <p>If unset, the query will search over all schema types.
*/
@NonNull
- public Builder addSchemaType(@NonNull Collection<String> schemaTypes) {
- Preconditions.checkNotNull(schemaTypes);
+ public Builder addFilterSchemas(@NonNull Collection<String> schemas) {
+ Preconditions.checkNotNull(schemas);
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mSchemaTypes.addAll(schemaTypes);
+ mSchemas.addAll(schemas);
return this;
}
@@ -339,10 +319,10 @@
* <p>If unset, the query will search over all namespaces.
*/
@NonNull
- public Builder addNamespace(@NonNull String... namespaces) {
+ public Builder addFilterNamespaces(@NonNull String... namespaces) {
Preconditions.checkNotNull(namespaces);
Preconditions.checkState(!mBuilt, "Builder has already been used");
- return addNamespace(Arrays.asList(namespaces));
+ return addFilterNamespaces(Arrays.asList(namespaces));
}
/**
@@ -352,7 +332,7 @@
* <p>If unset, the query will search over all namespaces.
*/
@NonNull
- public Builder addNamespace(@NonNull Collection<String> namespaces) {
+ public Builder addFilterNamespaces(@NonNull Collection<String> namespaces) {
Preconditions.checkNotNull(namespaces);
Preconditions.checkState(!mBuilt, "Builder has already been used");
mNamespaces.addAll(namespaces);
@@ -367,9 +347,6 @@
* package names are specified which caller doesn't have access to, then those package names
* will be ignored.
*/
- // Getter is called "getFilterPackageNames" (as opposed to the suggested
- // "getFilterPackageNameses")
- @SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addFilterPackageNames(@NonNull String... packageNames) {
Preconditions.checkNotNull(packageNames);
@@ -385,9 +362,6 @@
* package names are specified which caller doesn't have access to, then those package names
* will be ignored.
*/
- // Getter is called "getFilterPackageNames" (as opposed to the suggested
- // "getFilterPackageNameses")
- @SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addFilterPackageNames(@NonNull Collection<String> packageNames) {
Preconditions.checkNotNull(packageNames);
@@ -537,7 +511,7 @@
* type property paths:
*
* <pre>{@code
- * {schemaType: "Email", ["subject", "sender.name", "recipients.name"]}
+ * {schema: "Email", ["subject", "sender.name", "recipients.name"]}
* }</pre>
*
* <p>The above document will be returned as:
@@ -561,9 +535,9 @@
*/
@NonNull
public SearchSpec.Builder addProjection(
- @NonNull String schemaType, @NonNull String... propertyPaths) {
+ @NonNull String schema, @NonNull String... propertyPaths) {
Preconditions.checkNotNull(propertyPaths);
- return addProjection(schemaType, Arrays.asList(propertyPaths));
+ return addProjection(schema, Arrays.asList(propertyPaths));
}
/**
@@ -583,16 +557,16 @@
*/
@NonNull
public SearchSpec.Builder addProjection(
- @NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
+ @NonNull String schema, @NonNull Collection<String> propertyPaths) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(schemaType);
+ Preconditions.checkNotNull(schema);
Preconditions.checkNotNull(propertyPaths);
ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size());
for (String propertyPath : propertyPaths) {
Preconditions.checkNotNull(propertyPath);
propertyPathsArrayList.add(propertyPath);
}
- mProjectionTypePropertyMasks.putStringArrayList(schemaType, propertyPathsArrayList);
+ mProjectionTypePropertyMasks.putStringArrayList(schema, propertyPathsArrayList);
return this;
}
@@ -608,7 +582,7 @@
throw new IllegalSearchSpecException("Missing termMatchType field.");
}
mBundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces);
- mBundle.putStringArrayList(SCHEMA_TYPE_FIELD, mSchemaTypes);
+ mBundle.putStringArrayList(SCHEMA_FIELD, mSchemas);
mBundle.putStringArrayList(PACKAGE_NAME_FIELD, mPackageNames);
mBundle.putBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD, mProjectionTypePropertyMasks);
mBuilt = true;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index 1486df3..2caa94a5e 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -126,9 +126,9 @@
* <p>Any documents of these types will be visible on system UI surfaces by default.
*/
@NonNull
- public Builder addSchema(@NonNull AppSearchSchema... schemas) {
+ public Builder addSchemas(@NonNull AppSearchSchema... schemas) {
Preconditions.checkNotNull(schemas);
- return addSchema(Arrays.asList(schemas));
+ return addSchemas(Arrays.asList(schemas));
}
/**
@@ -137,7 +137,7 @@
* <p>Any documents of these types will be visible on system UI surfaces by default.
*/
@NonNull
- public Builder addSchema(@NonNull Collection<AppSearchSchema> schemas) {
+ public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(schemas);
mSchemas.addAll(schemas);
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 90a6f60..0328d54 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -179,9 +179,9 @@
/** Adds migratedTypes to the list of migrated schema types. */
@NonNull
- public Builder addMigratedType(@NonNull String migratedType) {
+ public Builder addMigratedType(@NonNull Collection<String> migratedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mMigratedTypes.add(Preconditions.checkNotNull(migratedType));
+ mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes));
return this;
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 592b8b9..8bff720 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -195,8 +195,7 @@
mIcingSearchEngineLocked = new IcingSearchEngine(options);
mVisibilityStoreLocked =
- new VisibilityStore(
- this, context, userId, globalQuerierPackage);
+ new VisibilityStore(this, context, userId, globalQuerierPackage);
InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
SchemaProto schemaProto;
@@ -508,8 +507,8 @@
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
- if (!searchSpec.getPackageNames().isEmpty()
- && !searchSpec.getPackageNames().contains(packageName)) {
+ List<String> filterPackageNames = searchSpec.getFilterPackageNames();
+ if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
// Client wanted to query over some packages that weren't its own. This isn't
// allowed through local query so we can return early with no results.
return new SearchResultPage(Bundle.EMPTY);
@@ -553,7 +552,7 @@
throws AppSearchException {
mReadWriteLock.readLock().lock();
try {
- Set<String> packageFilters = new ArraySet<>(searchSpec.getPackageNames());
+ Set<String> packageFilters = new ArraySet<>(searchSpec.getFilterPackageNames());
Set<String> prefixFilters = new ArraySet<>();
Set<String> allPrefixes = mNamespaceMapLocked.keySet();
if (packageFilters.isEmpty()) {
@@ -573,7 +572,7 @@
// Find which schemas the client is allowed to query over.
Set<String> allowedPrefixedSchemas = new ArraySet<>();
- List<String> schemaFilters = searchSpec.getSchemaTypes();
+ List<String> schemaFilters = searchSpec.getFilterSchemas();
for (String prefix : prefixFilters) {
String packageName = getPackageName(prefix);
@@ -752,8 +751,8 @@
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
- if (!searchSpec.getPackageNames().isEmpty()
- && !searchSpec.getPackageNames().contains(packageName)) {
+ List<String> filterPackageNames = searchSpec.getFilterPackageNames();
+ if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
// We're only removing documents within the parameter `packageName`. If we're not
// restricting our remove-query to this package name, then there's nothing for us to
// remove.
@@ -1085,7 +1084,7 @@
Set<String> allowedPrefixedSchemas = new ArraySet<>();
// Add all the schema filters the client specified.
- List<String> schemaFilters = searchSpec.getSchemaTypes();
+ List<String> schemaFilters = searchSpec.getFilterSchemas();
for (int i = 0; i < schemaFilters.size(); i++) {
allowedPrefixedSchemas.add(prefix + schemaFilters.get(i));
}
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
new file mode 100644
index 0000000..b3f8264
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
@@ -0,0 +1,166 @@
+/*
+ * 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;
+
+import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchMigrationHelper;
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.SearchResultPage;
+import android.app.appsearch.SearchSpec;
+import android.app.appsearch.SetSchemaResponse;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * An implementation of {@link AppSearchMigrationHelper} which query document and save post-migrated
+ * documents to locally in the app's storage space.
+ */
+class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper {
+ private final AppSearchImpl mAppSearchImpl;
+ private final String mPackageName;
+ private final String mDatabaseName;
+ private final File mFile;
+ private final Map<String, Integer> mCurrentVersionMap;
+ private final Map<String, Integer> mFinalVersionMap;
+
+ AppSearchMigrationHelperImpl(
+ @NonNull AppSearchImpl appSearchImpl,
+ @NonNull Map<String, Integer> currentVersionMap,
+ @NonNull Map<String, Integer> finalVersionMap,
+ @NonNull String packageName,
+ @NonNull String databaseName)
+ throws IOException {
+ mAppSearchImpl = Preconditions.checkNotNull(appSearchImpl);
+ mCurrentVersionMap = Preconditions.checkNotNull(currentVersionMap);
+ mFinalVersionMap = Preconditions.checkNotNull(finalVersionMap);
+ mPackageName = Preconditions.checkNotNull(packageName);
+ mDatabaseName = Preconditions.checkNotNull(databaseName);
+ mFile = File.createTempFile(/*prefix=*/ "appsearch", /*suffix=*/ null);
+ }
+
+ @Override
+ public void queryAndTransform(
+ @NonNull String schemaType, @NonNull AppSearchMigrationHelper.Transformer migrator)
+ throws Exception {
+ Preconditions.checkState(mFile.exists(), "Internal temp file does not exist.");
+ int currentVersion = mCurrentVersionMap.get(schemaType);
+ int finalVersion = mFinalVersionMap.get(schemaType);
+ try (FileOutputStream outputStream = new FileOutputStream(mFile)) {
+ // TODO(b/151178558) change the output stream so that we can use it in platform
+ CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream);
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.query(
+ mPackageName,
+ mDatabaseName,
+ /*queryExpression=*/ "",
+ new SearchSpec.Builder()
+ .addFilterSchemas(schemaType)
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .build());
+ while (!searchResultPage.getResults().isEmpty()) {
+ for (int i = 0; i < searchResultPage.getResults().size(); i++) {
+ GenericDocument newDocument =
+ migrator.transform(
+ currentVersion,
+ finalVersion,
+ searchResultPage.getResults().get(i).getDocument());
+ Bundle bundle = newDocument.getBundle();
+ Parcel parcel = Parcel.obtain();
+ parcel.writeBundle(bundle);
+ byte[] serializedMessage = parcel.marshall();
+ parcel.recycle();
+ codedOutputStream.writeByteArrayNoTag(serializedMessage);
+ }
+ codedOutputStream.flush();
+ searchResultPage = mAppSearchImpl.getNextPage(searchResultPage.getNextPageToken());
+ outputStream.flush();
+ }
+ }
+ }
+
+ /**
+ * Reads {@link GenericDocument} from the temperate file and saves them to AppSearch.
+ *
+ * <p>This method should be only called once.
+ *
+ * @return the {@link AppSearchBatchResult} for migration documents.
+ */
+ @NonNull
+ public SetSchemaResponse readAndPutDocuments(SetSchemaResponse.Builder responseBuilder)
+ throws IOException, AppSearchException {
+ Preconditions.checkState(mFile.exists(), "Internal temp file does not exist.");
+ try (InputStream inputStream = new FileInputStream(mFile)) {
+ CodedInputStream codedInputStream = CodedInputStream.newInstance(inputStream);
+ while (!codedInputStream.isAtEnd()) {
+ GenericDocument document = readDocumentFromInputStream(codedInputStream);
+ try {
+ mAppSearchImpl.putDocument(mPackageName, mDatabaseName, document);
+ } catch (Throwable t) {
+ responseBuilder.setFailure(
+ document.getSchemaType(),
+ document.getNamespace(),
+ document.getUri(),
+ throwableToFailedResult(t));
+ }
+ }
+ mAppSearchImpl.persistToDisk();
+ return responseBuilder.build();
+ } finally {
+ mFile.delete();
+ }
+ }
+
+ void deleteTempFile() {
+ mFile.delete();
+ }
+
+ /**
+ * Reads {@link GenericDocument} from given {@link CodedInputStream}.
+ *
+ * @param codedInputStream The codedInputStream to read from
+ * @throws IOException on File operation error.
+ */
+ @NonNull
+ private static GenericDocument readDocumentFromInputStream(
+ @NonNull CodedInputStream codedInputStream) throws IOException {
+ byte[] serializedMessage = codedInputStream.readByteArray();
+
+ Parcel parcel = Parcel.obtain();
+ parcel.unmarshall(serializedMessage, 0, serializedMessage.length);
+ parcel.setDataPosition(0);
+ Bundle bundle = parcel.readBundle();
+ parcel.recycle();
+
+ return new GenericDocument(bundle);
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
index 07d50ae..3b5e275 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
@@ -40,8 +40,8 @@
Preconditions.checkNotNull(spec);
SearchSpecProto.Builder protoBuilder =
SearchSpecProto.newBuilder()
- .addAllSchemaTypeFilters(spec.getSchemaTypes())
- .addAllNamespaceFilters(spec.getNamespaces());
+ .addAllSchemaTypeFilters(spec.getFilterSchemas())
+ .addAllNamespaceFilters(spec.getFilterNamespaces());
@SearchSpec.TermMatch int termMatchCode = spec.getTermMatch();
TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode);
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 2774181..12699b7 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I3fd4c96bf775c2539d744c416cdbf1d3c9544f03
+Ibe06fb9c574c8718191f833bb042fa10c300e4e2
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index f8d0d80..afa633a 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -102,10 +102,10 @@
@Override
@NonNull
- public ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments(
+ public ListenableFuture<AppSearchBatchResult<String, Void>> put(
@NonNull PutDocumentsRequest request) {
SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create();
- mAppSearchSession.putDocuments(
+ mAppSearchSession.put(
request, mExecutor, new BatchResultCallbackAdapter<>(future));
return future;
}
@@ -122,10 +122,10 @@
@Override
@NonNull
- public SearchResultsShim query(
+ public SearchResultsShim search(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
SearchResults searchResults =
- mAppSearchSession.query(queryExpression, searchSpec, mExecutor);
+ mAppSearchSession.search(queryExpression, searchSpec, mExecutor);
return new SearchResultsShimImpl(searchResults, mExecutor);
}
@@ -139,19 +139,19 @@
@Override
@NonNull
- public ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri(
+ public ListenableFuture<AppSearchBatchResult<String, Void>> remove(
@NonNull RemoveByUriRequest request) {
SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create();
- mAppSearchSession.removeByUri(request, mExecutor, new BatchResultCallbackAdapter<>(future));
+ mAppSearchSession.remove(request, mExecutor, new BatchResultCallbackAdapter<>(future));
return future;
}
@Override
@NonNull
- public ListenableFuture<Void> removeByQuery(
+ public ListenableFuture<Void> remove(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
- mAppSearchSession.removeByQuery(queryExpression, searchSpec, mExecutor, future::set);
+ mAppSearchSession.remove(queryExpression, searchSpec, mExecutor, future::set);
return Futures.transformAsync(future, this::transformResult, mExecutor);
}
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
index 39ca687..eb1623e 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
@@ -67,10 +67,10 @@
@NonNull
@Override
- public SearchResultsShim query(
+ public SearchResultsShim search(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
SearchResults searchResults =
- mGlobalSearchSession.query(queryExpression, searchSpec, mExecutor);
+ mGlobalSearchSession.search(queryExpression, searchSpec, mExecutor);
return new SearchResultsShimImpl(searchResults, mExecutor);
}
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 e8ea6ef..8e62c0e 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
@@ -33,7 +33,7 @@
public interface AppSearchSessionShim extends Closeable {
/**
- * Sets the schema that will be used by documents provided to the {@link #putDocuments} method.
+ * Sets the schema that will be used by documents provided to the {@link #put} method.
*
* <p>The schema provided here is compared to the stored copy of the schema previously supplied
* to {@link #setSchema}, if any, to determine how to treat existing documents. The following
@@ -143,8 +143,7 @@
* they were successfully indexed, or a failed {@link AppSearchResult} otherwise.
*/
@NonNull
- ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments(
- @NonNull PutDocumentsRequest request);
+ ListenableFuture<AppSearchBatchResult<String, Void>> put(@NonNull PutDocumentsRequest request);
/**
* Retrieves {@link GenericDocument}s by URI.
@@ -161,7 +160,7 @@
@NonNull GetByUriRequest request);
/**
- * Searches a document based on a given query string.
+ * Searches for documents based on a given query string.
*
* <p>Currently we support following features in the raw query format:
*
@@ -200,7 +199,7 @@
* @return The search result of performing this operation.
*/
@NonNull
- SearchResultsShim query(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+ SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
/**
* Reports usage of a particular document by URI and namespace.
@@ -208,7 +207,7 @@
* <p>A usage report represents an event in which a user interacted with or viewed a document.
*
* <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
- * metrics for that particular document. These metrics are used for ordering {@link #query}
+ * metrics for that particular document. These metrics are used for ordering {@link #search}
* results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and {@link
* SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
*
@@ -231,13 +230,13 @@
* {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}.
*/
@NonNull
- ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri(
+ ListenableFuture<AppSearchBatchResult<String, Void>> remove(
@NonNull RemoveByUriRequest request);
/**
* Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
* match the {@code queryExpression} in given namespaces and schemaTypes which is set via {@link
- * SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}.
+ * SearchSpec.Builder#addFilterNamespaces} and {@link SearchSpec.Builder#addFilterSchemas}.
*
* <p>An empty {@code queryExpression} matches all documents.
*
@@ -251,8 +250,7 @@
* @return The pending result of performing this operation.
*/
@NonNull
- ListenableFuture<Void> removeByQuery(
- @NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+ ListenableFuture<Void> remove(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
/**
* Flush all schema and document updates, additions, and deletes to disk if possible.
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 cd867a4..b96f99e 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
@@ -23,7 +23,7 @@
/**
* This class provides global access to the centralized AppSearch index maintained by the system.
*
- * <p>Apps can retrieve indexed documents through the query API.
+ * <p>Apps can retrieve indexed documents through the {@link #search} API.
*/
public interface GlobalSearchSessionShim extends Closeable {
/**
@@ -66,7 +66,7 @@
* @return The search result of performing this operation.
*/
@NonNull
- SearchResultsShim query(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+ SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
/** Closes the {@link GlobalSearchSessionShim}. */
@Override
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index ab87222..283e933 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -74,6 +74,27 @@
}
/**
+ * Allow the temp allowlist behavior, plus allow foreground service start from background.
+ */
+ public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+ /**
+ * Only allow the temp allowlist behavior, not allow foreground service start from
+ * background.
+ */
+ public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+
+ /**
+ * The list of temp allowlist types.
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "TEMPORARY_ALLOW_TYPE_" }, value = {
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TempAllowListType {}
+
+ /**
* @hide
*/
public PowerWhitelistManager(@NonNull Context context) {
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 4441643..e045b0f 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -16,7 +16,7 @@
package com.android.server;
-import android.app.BroadcastOptions;
+import android.os.PowerWhitelistManager.TempAllowListType;
import com.android.server.deviceidle.IDeviceIdleConstraint;
@@ -39,12 +39,12 @@
* allowlist.
* @param uid
* @param duration duration in milliseconds
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param type temp allowlist type defined at {@link TempAllowListType}
* @param sync
* @param reason
*/
void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
- @BroadcastOptions.TempAllowListType int type, boolean sync,
+ @TempAllowListType int type, boolean sync,
String reason);
// duration in milliseconds
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index c9427e9..8f7f705 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -22,7 +22,6 @@
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.BroadcastOptions;
-import android.app.BroadcastOptions.TempAllowListType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -57,6 +56,7 @@
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
+import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -3879,7 +3879,7 @@
* @param adding true to add to temp allowlist, false to remove from temp allowlist.
* @param durationMs duration in milliseconds to add to temp allowlist, only valid when
* param adding is true.
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param type temp allowlist type defined at {@link TempAllowListType}
*/
private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs,
@TempAllowListType int type) {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index d4ee9bb..f6a1b8a 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -151,7 +151,8 @@
static final boolean DEBUG_BG_LIMIT = localLOGV || false;
static final boolean DEBUG_STANDBY = localLOGV || false;
static final boolean RECORD_ALARMS_IN_HISTORY = true;
- static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
+ // TODO(b/178484639) : Turn off once alarms and reminders work is complete.
+ static final boolean RECORD_DEVICE_IDLE_ALARMS = true;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
static final int TICK_HISTORY_DEPTH = 10;
@@ -1731,8 +1732,8 @@
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = a.uid;
- ent.pkg = a.operation.getCreatorPackage();
- ent.tag = a.operation.getTag("");
+ ent.pkg = a.sourcePackage;
+ ent.tag = a.statsTag;
ent.op = "START IDLE";
ent.elapsedRealtime = mInjector.getElapsedRealtime();
ent.argRealtime = a.getWhenElapsed();
@@ -3151,8 +3152,8 @@
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = alarm.uid;
- ent.pkg = alarm.operation.getCreatorPackage();
- ent.tag = alarm.operation.getTag("");
+ ent.pkg = alarm.sourcePackage;
+ ent.tag = alarm.statsTag;
ent.op = "END IDLE";
ent.elapsedRealtime = mInjector.getElapsedRealtime();
ent.argRealtime = alarm.getWhenElapsed();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index e05f0b0..18856f7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -62,6 +62,7 @@
CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000;
+ // Try to give higher priority types lower values.
static final int WORK_TYPE_NONE = 0;
static final int WORK_TYPE_TOP = 1 << 0;
static final int WORK_TYPE_BG = 1 << 1;
@@ -520,6 +521,120 @@
}
}
+ void onJobCompletedLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
+ @WorkType final int workType) {
+ mWorkCountTracker.onJobFinished(workType);
+ mRunningJobs.remove(jobStatus);
+ final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
+ updateCounterConfigLocked();
+ // Preemption case needs special care.
+ updateNonRunningPriorities(pendingJobs, false);
+
+ JobStatus highestPriorityJob = null;
+ int highPriWorkType = workType;
+ JobStatus backupJob = null;
+ int backupWorkType = WORK_TYPE_NONE;
+ for (int i = 0; i < pendingJobs.size(); i++) {
+ final JobStatus nextPending = pendingJobs.get(i);
+
+ if (mRunningJobs.contains(nextPending)) {
+ continue;
+ }
+
+ if (worker.getPreferredUid() != nextPending.getUid()) {
+ if (backupJob == null) {
+ int workAsType =
+ mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ if (workAsType != WORK_TYPE_NONE) {
+ backupJob = nextPending;
+ backupWorkType = workAsType;
+ }
+ }
+ continue;
+ }
+
+ if (highestPriorityJob == null
+ || highestPriorityJob.lastEvaluatedPriority
+ < nextPending.lastEvaluatedPriority) {
+ highestPriorityJob = nextPending;
+ } else {
+ continue;
+ }
+
+ // In this path, we pre-empted an existing job. We don't fully care about the
+ // reserved slots. We should just run the highest priority job we can find,
+ // though it would be ideal to use an available WorkType slot instead of
+ // overloading slots.
+ final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ if (workAsType == WORK_TYPE_NONE) {
+ // Just use the preempted job's work type since this new one is technically
+ // replacing it anyway.
+ highPriWorkType = workType;
+ } else {
+ highPriWorkType = workAsType;
+ }
+ }
+ if (highestPriorityJob != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Running job " + jobStatus + " as preemption");
+ }
+ mWorkCountTracker.stageJob(highPriWorkType);
+ startJobLocked(worker, highestPriorityJob, highPriWorkType);
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Couldn't find preemption job for uid " + worker.getPreferredUid());
+ }
+ worker.clearPreferredUid();
+ if (backupJob != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Running job " + jobStatus + " instead");
+ }
+ mWorkCountTracker.stageJob(backupWorkType);
+ startJobLocked(worker, backupJob, backupWorkType);
+ }
+ }
+ } else if (pendingJobs.size() > 0) {
+ updateCounterConfigLocked();
+ updateNonRunningPriorities(pendingJobs, false);
+
+ // This slot is now free and we have pending jobs. Start the highest priority job we
+ // find.
+ JobStatus highestPriorityJob = null;
+ int highPriWorkType = workType;
+ for (int i = 0; i < pendingJobs.size(); i++) {
+ final JobStatus nextPending = pendingJobs.get(i);
+
+ if (mRunningJobs.contains(nextPending)) {
+ continue;
+ }
+
+ final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ if (workAsType == WORK_TYPE_NONE) {
+ continue;
+ }
+ if (highestPriorityJob == null
+ || highestPriorityJob.lastEvaluatedPriority
+ < nextPending.lastEvaluatedPriority) {
+ highestPriorityJob = nextPending;
+ highPriWorkType = workAsType;
+ }
+ }
+
+ if (highestPriorityJob != null) {
+ // This slot is free, and we haven't yet hit the limit on
+ // concurrent jobs... we can just throw the job in to here.
+ if (DEBUG) {
+ Slog.d(TAG, "About to run job: " + jobStatus);
+ }
+ mWorkCountTracker.stageJob(highPriWorkType);
+ startJobLocked(worker, highestPriorityJob, highPriWorkType);
+ }
+ }
+
+ noteConcurrency();
+ }
+
@GuardedBy("mLock")
private String printPendingQueueLocked() {
StringBuilder s = new StringBuilder("Pending queue: ");
@@ -665,9 +780,18 @@
WorkTypeConfig(@NonNull String configIdentifier, int defaultMaxTotal,
List<Pair<Integer, Integer>> defaultMin, List<Pair<Integer, Integer>> defaultMax) {
mConfigIdentifier = configIdentifier;
- mDefaultMaxTotal = mMaxTotal = defaultMaxTotal;
+ mDefaultMaxTotal = mMaxTotal = Math.min(defaultMaxTotal, MAX_JOB_CONTEXTS_COUNT);
+ int numReserved = 0;
for (int i = defaultMin.size() - 1; i >= 0; --i) {
mDefaultMinReservedSlots.put(defaultMin.get(i).first, defaultMin.get(i).second);
+ numReserved += defaultMin.get(i).second;
+ }
+ if (mDefaultMaxTotal < 0 || numReserved > mDefaultMaxTotal) {
+ // We only create new configs on boot, so this should trigger during development
+ // (before the code gets checked in), so this makes sure the hard-coded defaults
+ // make sense. DeviceConfig values will be handled gracefully in update().
+ throw new IllegalArgumentException("Invalid default config: t=" + defaultMaxTotal
+ + " min=" + defaultMin + " max=" + defaultMax);
}
for (int i = defaultMax.size() - 1; i >= 0; --i) {
mDefaultMaxAllowedSlots.put(defaultMax.get(i).first, defaultMax.get(i).second);
@@ -782,15 +906,10 @@
mNumUnspecialized = mConfigMaxTotal;
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
- mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP);
- mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG);
- calculateUnspecializedRemaining();
- }
-
- private void calculateUnspecializedRemaining() {
- mNumUnspecializedRemaining = mNumUnspecialized;
+ mNumUnspecializedRemaining = mConfigMaxTotal;
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
- mNumUnspecializedRemaining -= mNumRunningJobs.valueAt(i);
+ mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i),
+ mConfigNumReservedSlots.get(mNumRunningJobs.keyAt(i)));
}
}
@@ -851,7 +970,25 @@
if (numRemainingForType < mNumActuallyReservedSlots.get(workType)) {
// We've run all jobs for this type. Let another type use it now.
mNumActuallyReservedSlots.put(workType, numRemainingForType);
- mNumUnspecializedRemaining++;
+ int assignWorkType = WORK_TYPE_NONE;
+ for (int i = 0; i < mNumActuallyReservedSlots.size(); ++i) {
+ int wt = mNumActuallyReservedSlots.keyAt(i);
+ if (assignWorkType == WORK_TYPE_NONE || wt < assignWorkType) {
+ // Try to give this slot to the highest priority one within its limits.
+ int total = mNumRunningJobs.get(wt) + mNumStartingJobs.get(wt)
+ + mNumPendingJobs.get(wt);
+ if (mNumActuallyReservedSlots.valueAt(i) < mConfigAbsoluteMaxSlots.get(wt)
+ && total > mNumActuallyReservedSlots.valueAt(i)) {
+ assignWorkType = wt;
+ }
+ }
+ }
+ if (assignWorkType != WORK_TYPE_NONE) {
+ mNumActuallyReservedSlots.put(assignWorkType,
+ mNumActuallyReservedSlots.get(assignWorkType) + 1);
+ } else {
+ mNumUnspecializedRemaining++;
+ }
}
}
@@ -867,30 +1004,60 @@
}
}
+ void onJobFinished(@WorkType int workType) {
+ final int newNumRunningJobs = mNumRunningJobs.get(workType) - 1;
+ if (newNumRunningJobs < 0) {
+ // We are in a bad state. We will eventually recover when the pending list is
+ // regenerated.
+ Slog.e(TAG, "# running jobs for " + workType + " went negative.");
+ return;
+ }
+ mNumRunningJobs.put(workType, newNumRunningJobs);
+ maybeAdjustReservations(workType);
+ }
+
void onCountDone() {
// Calculate how many slots to reserve for each work type. "Unspecialized" slots will
// be reserved for higher importance types first (ie. top before bg).
mNumUnspecialized = mConfigMaxTotal;
final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
+ mNumPendingJobs.get(WORK_TYPE_TOP);
- final int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
+ int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
mNumUnspecialized -= resTop;
final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
- final int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
+ int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
mNumUnspecialized -= resBg;
- calculateUnspecializedRemaining();
+
+ mNumUnspecializedRemaining = mNumUnspecialized;
+ // Account for already running jobs after we've assigned the minimum number of slots.
+ int unspecializedAssigned;
+ int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop);
+ if (extraRunning > 0) {
+ unspecializedAssigned = Math.max(0,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop,
+ extraRunning));
+ resTop += unspecializedAssigned;
+ mNumUnspecializedRemaining -= extraRunning;
+ }
+ extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg);
+ if (extraRunning > 0) {
+ unspecializedAssigned = Math.max(0,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning));
+ resBg += unspecializedAssigned;
+ mNumUnspecializedRemaining -= extraRunning;
+ }
// Assign remaining unspecialized based on ranking.
- int unspecializedAssigned = Math.max(0,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP),
- Math.min(mNumUnspecializedRemaining, numTop - resTop)));
+ unspecializedAssigned = Math.max(0,
+ Math.min(mNumUnspecializedRemaining,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop));
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
unspecializedAssigned = Math.max(0,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
- Math.min(mNumUnspecializedRemaining, numBg - resBg)));
+ Math.min(mNumUnspecializedRemaining,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
}
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 ba78bda..7ce867c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1426,8 +1426,8 @@
// Create the "runners".
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
mActiveServices.add(
- new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
- getContext().getMainLooper()));
+ new JobServiceContext(this, mConcurrencyManager, mBatteryStats,
+ mJobPackageTracker, getContext().getMainLooper()));
}
// Attach jobs to their controllers.
mJobs.forEachJob((job) -> {
@@ -1710,9 +1710,6 @@
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
- // We still want to check for jobs to execute, because this job may have
- // scheduled a new job under the same job id, and now we can run it.
- mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
return;
}
@@ -1734,7 +1731,6 @@
}
jobStatus.unprepareLocked();
reportActiveLocked();
- mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
}
// StateChangedListener implementations.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 247b421..d15bae0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -107,6 +107,7 @@
private final Handler mCallbackHandler;
/** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
private final JobCompletedListener mCompletedListener;
+ private final JobConcurrencyManager mJobConcurrencyManager;
/** Used for service binding, etc. */
private final Context mContext;
private final Object mLock;
@@ -183,13 +184,14 @@
}
}
- JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
- JobPackageTracker tracker, Looper looper) {
+ JobServiceContext(JobSchedulerService service, JobConcurrencyManager concurrencyManager,
+ IBatteryStats batteryStats, JobPackageTracker tracker, Looper looper) {
mContext = service.getContext();
mLock = service.getLock();
mBatteryStats = batteryStats;
mJobPackageTracker = tracker;
mCallbackHandler = new JobServiceHandler(looper);
+ mJobConcurrencyManager = concurrencyManager;
mCompletedListener = service;
mAvailable = true;
mVerb = VERB_FINISHED;
@@ -835,6 +837,7 @@
if (mWakeLock != null) {
mWakeLock.release();
}
+ final int workType = mRunningJobWorkType;
mContext.unbindService(JobServiceContext.this);
mWakeLock = null;
mRunningJob = null;
@@ -847,6 +850,7 @@
mAvailable = true;
removeOpTimeOutLocked();
mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
+ mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType);
}
private void applyStoppedReasonLocked(String reason) {
diff --git a/core/java/android/net/VpnInfo.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
similarity index 73%
copy from core/java/android/net/VpnInfo.aidl
copy to apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
index 8bcaa81..3d50d14 100644
--- a/core/java/android/net/VpnInfo.aidl
+++ b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
+/**
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.media;
-package android.net;
+/** {@hide} */
+interface IMediaCommunicationService {
+}
-parcelable VpnInfo;
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 60dea07..5773e4d 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -38,6 +38,7 @@
static_libs: [
"exoplayer2-extractor",
"mediatranscoding_aidl_interface-java",
+ "modules-utils-build",
],
jarjar_rules: "jarjar_rules.txt",
@@ -52,6 +53,7 @@
visibility: [
"//frameworks/av/apex:__subpackages__",
"//frameworks/base", // For framework-all
+ "//frameworks/base/apex/media/service",
],
}
@@ -80,6 +82,7 @@
"java/android/media/Session2CommandGroup.java",
"java/android/media/Session2Link.java",
"java/android/media/Session2Token.java",
+ "java/android/media/MediaCommunicationManager.java",
],
path: "java",
}
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index 2543a9c..8b9990f 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -28,6 +28,9 @@
ctor public ApplicationMediaCapabilities.FormatNotFoundException(@NonNull String);
}
+ public class MediaCommunicationManager {
+ }
+
public class MediaController2 implements java.lang.AutoCloseable {
method public void cancelSessionCommand(@NonNull Object);
method public void close();
diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
index d89d9d3..eb71fdd 100644
--- a/apex/media/framework/jarjar_rules.txt
+++ b/apex/media/framework/jarjar_rules.txt
@@ -1 +1,2 @@
+rule com.android.modules.utils.** android.media.internal.utils.@1
rule com.google.android.exoplayer2.** android.media.internal.exo.@1
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
new file mode 100644
index 0000000..b8065ef
--- /dev/null
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+
+import com.android.modules.utils.build.SdkLevel;
+
+/**
+ * Provides support for interacting with {@link android.media.MediaSession2 MediaSession2s}
+ * that applications have published to express their ongoing media playback state.
+ */
+// TODO: Add notifySession2Created() and sendMessage().
+@SystemService(Context.MEDIA_COMMUNICATION_SERVICE)
+public class MediaCommunicationManager {
+ private static final String TAG = "MediaCommunicationManager";
+
+ private final Context mContext;
+ private final IMediaCommunicationService mService;
+
+ /**
+ * @hide
+ */
+ public MediaCommunicationManager(@NonNull Context context) {
+ if (!SdkLevel.isAtLeastS()) {
+ throw new UnsupportedOperationException("Android version must be S or greater.");
+ }
+ mContext = context;
+ mService = IMediaCommunicationService.Stub.asInterface(
+ MediaFrameworkInitializer.getMediaServiceManager()
+ .getMediaCommunicationServiceRegisterer()
+ .get());
+ }
+}
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
index 813ad7b..9332835 100644
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
@@ -19,10 +19,11 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
-import android.media.MediaTranscodeManager;
import android.app.SystemServiceRegistry;
import android.content.Context;
+import com.android.modules.utils.build.SdkLevel;
+
/**
* Class for performing registration for all media services on com.android.media apex.
*
@@ -74,5 +75,12 @@
MediaTranscodeManager.class,
context -> new MediaTranscodeManager(context)
);
+ if (SdkLevel.isAtLeastS()) {
+ SystemServiceRegistry.registerContextAwareService(
+ Context.MEDIA_COMMUNICATION_SERVICE,
+ MediaCommunicationManager.class,
+ context -> new MediaCommunicationManager(context)
+ );
+ }
}
}
diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp
new file mode 100644
index 0000000..5b24cfa
--- /dev/null
+++ b/apex/media/service/Android.bp
@@ -0,0 +1,41 @@
+// 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.
+filegroup {
+ name: "service-media-s-sources",
+ srcs: [
+ "java/**/*.java",
+ ],
+ path: "java",
+ visibility: ["//frameworks/base/services"], // TODO(b/177640454): Should be private.
+}
+
+java_sdk_library {
+ name: "service-media-s",
+ permitted_packages: [
+ "com.android.server.media",
+ ],
+ defaults: ["framework-system-server-module-defaults"],
+ srcs: [
+ ":service-media-s-sources",
+ ],
+ libs: [
+ "updatable-media",
+ ],
+ sdk_version: "system_server_current",
+ min_sdk_version: "29", // TODO: We may need to bump this at some point.
+ apex_available: [
+ "com.android.media",
+ ],
+}
+
diff --git a/apex/media/service/api/current.txt b/apex/media/service/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/service/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/service/api/removed.txt b/apex/media/service/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/service/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/service/api/system-server-current.txt b/apex/media/service/api/system-server-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/service/api/system-server-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/service/api/system-server-removed.txt b/apex/media/service/api/system-server-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/service/api/system-server-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
new file mode 100644
index 0000000..0468fdf
--- /dev/null
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -0,0 +1,41 @@
+/*
+ * 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.media;
+
+import android.content.Context;
+import android.media.IMediaCommunicationService;
+
+import com.android.server.SystemService;
+
+/**
+ * A system service that managers {@link android.media.MediaSession2} creations
+ * and their ongoing media playback state.
+ * @hide
+ */
+public class MediaCommunicationService extends SystemService {
+
+ public MediaCommunicationService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.MEDIA_COMMUNICATION_SERVICE, new Stub());
+ }
+
+ private class Stub extends IMediaCommunicationService.Stub {
+ }
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index b49e724..731d1a9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -157,7 +157,7 @@
field public static final String SET_WALLPAPER = "android.permission.SET_WALLPAPER";
field public static final String SET_WALLPAPER_HINTS = "android.permission.SET_WALLPAPER_HINTS";
field public static final String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
- field public static final String SMS_FINANCIAL_TRANSACTIONS = "android.permission.SMS_FINANCIAL_TRANSACTIONS";
+ field @Deprecated public static final String SMS_FINANCIAL_TRANSACTIONS = "android.permission.SMS_FINANCIAL_TRANSACTIONS";
field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE";
field public static final String STATUS_BAR = "android.permission.STATUS_BAR";
field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
@@ -722,6 +722,7 @@
field public static final int gwpAsanMode = 16844310; // 0x1010616
field public static final int hand_hour = 16843011; // 0x1010103
field public static final int hand_minute = 16843012; // 0x1010104
+ field public static final int hand_second = 16844323; // 0x1010623
field public static final int handle = 16843354; // 0x101025a
field public static final int handleProfiling = 16842786; // 0x1010022
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
@@ -1608,6 +1609,8 @@
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
field public static final int windowBackgroundFallback = 16844035; // 0x1010503
+ field public static final int windowBlurBehindEnabled = 16844316; // 0x101061c
+ field public static final int windowBlurBehindRadius = 16844315; // 0x101061b
field public static final int windowClipToOutline = 16843947; // 0x10104ab
field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
field public static final int windowContentOverlay = 16842841; // 0x1010059
@@ -7311,6 +7314,7 @@
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+ field public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1; // 0x1
field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -7464,6 +7468,7 @@
public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
method public int describeContents();
+ method public int getReason();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnsafeStateException> CREATOR;
}
@@ -8350,6 +8355,7 @@
method public android.appwidget.AppWidgetProviderInfo clone();
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 public final android.graphics.drawable.Drawable loadIcon(@NonNull android.content.Context, int);
method public final String loadLabel(android.content.pm.PackageManager);
@@ -9278,7 +9284,7 @@
method public boolean getIncludeTxPowerLevel();
method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData();
- method @Nullable public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
+ method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
method public java.util.List<android.os.ParcelUuid> getServiceUuids();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR;
@@ -10374,6 +10380,7 @@
field public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
field public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
field public static final String LOCATION_SERVICE = "location";
+ field public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
field public static final String MEDIA_METRICS_SERVICE = "media_metrics";
field public static final String MEDIA_PROJECTION_SERVICE = "media_projection";
field public static final String MEDIA_ROUTER_SERVICE = "media_router";
@@ -11867,6 +11874,7 @@
}
public class LauncherActivityInfo {
+ method @NonNull public android.content.pm.ActivityInfo getActivityInfo();
method public android.content.pm.ApplicationInfo getApplicationInfo();
method public android.graphics.drawable.Drawable getBadgedIcon(int);
method public android.content.ComponentName getComponentName();
@@ -12239,6 +12247,7 @@
method @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForActivity(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo, @Nullable android.content.res.Configuration) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
@@ -12350,6 +12359,8 @@
field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
+ field public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key";
+ field public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY = "android.hardware.keystore.single_use_key";
field public static final String FEATURE_LEANBACK = "android.software.leanback";
field public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
field public static final String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -12467,6 +12478,7 @@
field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
field public static final int PERMISSION_DENIED = -1; // 0xffffffff
field public static final int PERMISSION_GRANTED = 0; // 0x0
+ field public static final String PROPERTY_MEDIA_CAPABILITIES = "android.media.PROPERTY_MEDIA_CAPABILITIES";
field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
field public static final int SIGNATURE_MATCH = 0; // 0x0
field public static final int SIGNATURE_NEITHER_SIGNED = 1; // 0x1
@@ -16799,6 +16811,18 @@
package android.hardware {
+ public abstract class Battery {
+ ctor public Battery();
+ method @FloatRange(from=-1.0F, to=1.0f) public abstract float getCapacity();
+ method public abstract int getStatus();
+ method public abstract boolean hasBattery();
+ field public static final int STATUS_CHARGING = 2; // 0x2
+ field public static final int STATUS_DISCHARGING = 3; // 0x3
+ field public static final int STATUS_FULL = 5; // 0x5
+ field public static final int STATUS_NOT_CHARGING = 4; // 0x4
+ field public static final int STATUS_UNKNOWN = 1; // 0x1
+ }
+
@Deprecated public class Camera {
method @Deprecated public final void addCallbackBuffer(byte[]);
method @Deprecated public final void autoFocus(android.hardware.Camera.AutoFocusCallback);
@@ -18499,6 +18523,7 @@
public final class InputManager {
method public android.view.InputDevice getInputDevice(int);
method public int[] getInputDeviceIds();
+ method public float getMaximumObscuringOpacityForTouch();
method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler);
method public void unregisterInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener);
method @Nullable public android.view.VerifiedInputEvent verifyInputEvent(@NonNull android.view.InputEvent);
@@ -19848,6 +19873,10 @@
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
+ field public static final int ENCODING_MPEGH_BL_L3 = 23; // 0x17
+ field public static final int ENCODING_MPEGH_BL_L4 = 24; // 0x18
+ field public static final int ENCODING_MPEGH_LC_L3 = 25; // 0x19
+ field public static final int ENCODING_MPEGH_LC_L4 = 26; // 0x1a
field public static final int ENCODING_OPUS = 20; // 0x14
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_24BIT_PACKED = 21; // 0x15
@@ -20158,7 +20187,7 @@
}
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
- ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+ ctor @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method @Deprecated public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
method protected void finalize();
@@ -20219,7 +20248,7 @@
public static class AudioRecord.Builder {
ctor public AudioRecord.Builder();
- method public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException;
+ method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException;
method public android.media.AudioRecord.Builder setAudioFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException;
method @NonNull public android.media.AudioRecord.Builder setAudioPlaybackCaptureConfig(@NonNull android.media.AudioPlaybackCaptureConfiguration);
method public android.media.AudioRecord.Builder setAudioSource(int) throws java.lang.IllegalArgumentException;
@@ -21089,7 +21118,9 @@
public static final class MediaCodecInfo.AudioCapabilities {
method public android.util.Range<java.lang.Integer> getBitrateRange();
+ method @NonNull public android.util.Range<java.lang.Integer>[] getInputChannelCountRanges();
method public int getMaxInputChannelCount();
+ method public int getMinInputChannelCount();
method public android.util.Range<java.lang.Integer>[] getSupportedSampleRateRanges();
method public int[] getSupportedSampleRates();
method public boolean isSampleRateSupported(int);
@@ -25630,8 +25661,6 @@
method public void applyTransportModeTransform(@NonNull java.net.Socket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.net.DatagramSocket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.io.FileDescriptor, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @NonNull @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public void removeTransportModeTransforms(@NonNull java.net.Socket) throws java.io.IOException;
@@ -25641,12 +25670,6 @@
field public static final int DIRECTION_OUT = 1; // 0x1
}
- public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
- method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method public void close();
- method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- }
-
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
}
@@ -31853,6 +31876,10 @@
public final class ImplicitDirectBootViolation extends android.os.strictmode.Violation {
}
+ public final class IncorrectContextUseViolation extends android.os.strictmode.Violation {
+ ctor public IncorrectContextUseViolation(@NonNull String, @NonNull Throwable);
+ }
+
public class InstanceCountViolation extends android.os.strictmode.Violation {
method public long getNumberOfInstances();
}
@@ -34954,6 +34981,55 @@
field public static final String PATH_SETTING_INTENT = "intent";
}
+ public final class SimPhonebookContract {
+ field public static final String AUTHORITY = "com.android.simphonebook";
+ field @NonNull public static final android.net.Uri AUTHORITY_URI;
+ }
+
+ public static final class SimPhonebookContract.ElementaryFiles {
+ method @NonNull public static android.net.Uri getItemUri(int, int);
+ field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-elementary-file";
+ field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+ field @NonNull public static final android.net.Uri CONTENT_URI;
+ field public static final int EF_ADN = 1; // 0x1
+ field public static final int EF_FDN = 2; // 0x2
+ field public static final int EF_SDN = 3; // 0x3
+ field public static final String EF_TYPE = "ef_type";
+ field public static final int EF_UNKNOWN = 0; // 0x0
+ field public static final String MAX_RECORDS = "max_records";
+ field public static final String NAME_MAX_LENGTH = "name_max_length";
+ field public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+ field public static final String RECORD_COUNT = "record_count";
+ field public static final String SLOT_INDEX = "slot_index";
+ field public static final String SUBSCRIPTION_ID = "subscription_id";
+ }
+
+ public static final class SimPhonebookContract.SimRecords {
+ method @NonNull public static android.net.Uri getContentUri(int, int);
+ method @NonNull public static android.net.Uri getItemUri(int, int, int);
+ method @NonNull @WorkerThread public static android.provider.SimPhonebookContract.SimRecords.NameValidationResult validateName(@NonNull android.content.ContentResolver, int, int, @NonNull String);
+ field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+ field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+ field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+ field public static final String NAME = "name";
+ field public static final String PHONE_NUMBER = "phone_number";
+ field public static final String RECORD_NUMBER = "record_number";
+ field public static final String SUBSCRIPTION_ID = "subscription_id";
+ }
+
+ public static final class SimPhonebookContract.SimRecords.NameValidationResult implements android.os.Parcelable {
+ ctor public SimPhonebookContract.SimRecords.NameValidationResult(@NonNull String, @NonNull String, int, int);
+ method public int describeContents();
+ method public int getEncodedLength();
+ method public int getMaxEncodedLength();
+ method @NonNull public String getName();
+ method @NonNull public String getSanitizedName();
+ method public boolean isSupportedCharacter(int);
+ method public boolean isValid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.provider.SimPhonebookContract.SimRecords.NameValidationResult> CREATOR;
+ }
+
public class SyncStateContract {
ctor public SyncStateContract();
}
@@ -36850,6 +36926,7 @@
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
method @NonNull public String getKeystoreAlias();
+ method public int getMaxUsageCount();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36886,6 +36963,7 @@
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -36908,6 +36986,7 @@
method public String getKeystoreAlias();
method public int getOrigin();
method public int getPurposes();
+ method public int getRemainingUsageCount();
method public int getSecurityLevel();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36977,6 +37056,7 @@
field public static final int SECURITY_LEVEL_UNKNOWN_SECURE = -1; // 0xffffffff
field public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
field public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
+ field public static final int UNRESTRICTED_USAGE_COUNT = -1; // 0xffffffff
}
public final class KeyProtection implements java.security.KeyStore.ProtectionParameter {
@@ -36986,6 +37066,7 @@
method @Nullable public java.util.Date getKeyValidityForConsumptionEnd();
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
+ method public int getMaxUsageCount();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -37012,6 +37093,7 @@
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForConsumptionEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
+ method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
@@ -46123,6 +46205,7 @@
method @Deprecated public void getRectSize(android.graphics.Rect);
method public float getRefreshRate();
method public int getRotation();
+ method @Nullable public android.view.RoundedCorner getRoundedCorner(int);
method @Deprecated public void getSize(android.graphics.Point);
method public int getState();
method public android.view.Display.Mode[] getSupportedModes();
@@ -46366,6 +46449,7 @@
public final class InputDevice implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public android.hardware.Battery getBattery();
method public int getControllerNumber();
method public String getDescriptor();
method public static android.view.InputDevice getDevice(int);
@@ -46420,6 +46504,7 @@
field public static final int SOURCE_MOUSE = 8194; // 0x2002
field public static final int SOURCE_MOUSE_RELATIVE = 131076; // 0x20004
field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+ field public static final int SOURCE_SENSOR = 67108864; // 0x4000000
field public static final int SOURCE_STYLUS = 16386; // 0x4002
field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
@@ -47375,6 +47460,20 @@
field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
}
+ public final class RoundedCorner implements android.os.Parcelable {
+ ctor public RoundedCorner(int, int, int, int);
+ method public int describeContents();
+ method @NonNull public android.graphics.Point getCenter();
+ method public int getPosition();
+ method public int getRadius();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.RoundedCorner> CREATOR;
+ field public static final int POSITION_BOTTOM_LEFT = 3; // 0x3
+ field public static final int POSITION_BOTTOM_RIGHT = 2; // 0x2
+ field public static final int POSITION_TOP_LEFT = 0; // 0x0
+ field public static final int POSITION_TOP_RIGHT = 1; // 0x1
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -49384,6 +49483,7 @@
method @NonNull public android.graphics.Insets getInsets(int);
method @NonNull public android.graphics.Insets getInsetsIgnoringVisibility(int);
method @Deprecated @NonNull public android.graphics.Insets getMandatorySystemGestureInsets();
+ method @Nullable public android.view.RoundedCorner getRoundedCorner(int);
method @Deprecated public int getStableInsetBottom();
method @Deprecated public int getStableInsetLeft();
method @Deprecated public int getStableInsetRight();
@@ -49417,6 +49517,7 @@
method @NonNull public android.view.WindowInsets.Builder setInsets(int, @NonNull android.graphics.Insets);
method @NonNull public android.view.WindowInsets.Builder setInsetsIgnoringVisibility(int, @NonNull android.graphics.Insets) throws java.lang.IllegalArgumentException;
method @Deprecated @NonNull public android.view.WindowInsets.Builder setMandatorySystemGestureInsets(@NonNull android.graphics.Insets);
+ method @NonNull public android.view.WindowInsets.Builder setRoundedCorner(int, @Nullable android.view.RoundedCorner);
method @Deprecated @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets);
method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemGestureInsets(@NonNull android.graphics.Insets);
method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemWindowInsets(@NonNull android.graphics.Insets);
@@ -49569,6 +49670,7 @@
field public static final int FLAGS_CHANGED = 4; // 0x4
field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1
field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000
+ field public static final int FLAG_BLUR_BEHIND = 4; // 0x4
field public static final int FLAG_DIM_BEHIND = 2; // 0x2
field @Deprecated public static final int FLAG_DISMISS_KEYGUARD = 4194304; // 0x400000
field @Deprecated public static final int FLAG_DITHER = 4096; // 0x1000
@@ -49659,6 +49761,7 @@
field @Deprecated public static final int TYPE_TOAST = 2005; // 0x7d5
field public static final int TYPE_WALLPAPER = 2013; // 0x7dd
field public float alpha;
+ field public int blurBehindRadius;
field public float buttonBrightness;
field public float dimAmount;
field public int flags;
@@ -53061,6 +53164,12 @@
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 String getTimeZone();
+ method @Deprecated public void setDial(@NonNull android.graphics.drawable.Icon);
+ method @Deprecated public void setHourHand(@NonNull android.graphics.drawable.Icon);
+ method @Deprecated public void setMinuteHand(@NonNull android.graphics.drawable.Icon);
+ method @Deprecated public void setSecondHand(@Nullable android.graphics.drawable.Icon);
+ method @Deprecated public void setTimeZone(@Nullable String);
}
public class ArrayAdapter<T> extends android.widget.BaseAdapter implements android.widget.Filterable android.widget.ThemedSpinnerAdapter {
@@ -54468,19 +54577,26 @@
method public void setByte(@IdRes int, String, byte);
method public void setChar(@IdRes int, String, char);
method public void setCharSequence(@IdRes int, String, CharSequence);
+ method public void setCharSequence(@IdRes int, @NonNull String, @StringRes int);
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 setColorStateList(@IdRes int, @NonNull String, @ColorRes int);
method public void setContentDescription(@IdRes int, CharSequence);
method public void setDisplayedChild(@IdRes int, int);
method public void setDouble(@IdRes int, String, double);
method public void setEmptyView(@IdRes int, @IdRes int);
method public void setFloat(@IdRes int, String, float);
+ 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 setImageViewBitmap(@IdRes int, android.graphics.Bitmap);
method public void setImageViewIcon(@IdRes int, android.graphics.drawable.Icon);
method public void setImageViewResource(@IdRes int, @DrawableRes int);
method public void setImageViewUri(@IdRes int, android.net.Uri);
method public void setInt(@IdRes int, String, int);
+ method public void setIntDimen(@IdRes int, @NonNull String, @DimenRes int);
+ method public void setIntDimen(@IdRes int, @NonNull String, float, int);
method public void setIntent(@IdRes int, String, android.content.Intent);
method public void setLabelFor(@IdRes int, @IdRes int);
method public void setLightBackgroundLayoutId(@LayoutRes int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 0915d07..bf70803 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -103,6 +103,7 @@
}
public class MediaServiceManager {
+ method @NonNull public android.media.MediaServiceManager.ServiceRegisterer getMediaCommunicationServiceRegisterer();
method @NonNull public android.media.MediaServiceManager.ServiceRegisterer getMediaSessionServiceRegisterer();
method @NonNull public android.media.MediaServiceManager.ServiceRegisterer getMediaTranscodingServiceRegisterer();
}
@@ -156,7 +157,12 @@
package android.net {
+ public final class ConnectivityFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ }
+
public class ConnectivityManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
}
@@ -164,10 +170,22 @@
method public int getResourceId();
}
+ public final class NetworkAgentConfig implements android.os.Parcelable {
+ method @Nullable public String getSubscriberId();
+ }
+
+ public static final class NetworkAgentConfig.Builder {
+ method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
+ }
+
public final class NetworkCapabilities implements android.os.Parcelable {
field public static final int TRANSPORT_TEST = 7; // 0x7
}
+ public final class Proxy {
+ method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
+ }
+
public final class TcpRepairWindow {
ctor public TcpRepairWindow(int, int, int, int, int, int);
field public final int maxWindow;
@@ -194,6 +212,16 @@
method public void teardownTestNetwork(@NonNull android.net.Network);
}
+ public final class UnderlyingNetworkInfo implements android.os.Parcelable {
+ ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
+ field @NonNull public final String iface;
+ field public final int ownerUid;
+ field @NonNull public final java.util.List<java.lang.String> underlyingIfaces;
+ }
+
}
package android.os {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0a0f77e..3a7571c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -138,6 +138,7 @@
field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER";
field public static final String MANAGE_SEARCH_UI = "android.permission.MANAGE_SEARCH_UI";
field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
+ field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE";
field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
field public static final String MANAGE_TEST_NETWORKS = "android.permission.MANAGE_TEST_NETWORKS";
@@ -262,7 +263,6 @@
field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
- field public static final String USE_BACKGROUND_BLUR = "android.permission.USE_BACKGROUND_BLUR";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
@@ -294,8 +294,6 @@
field public static final int sdkVersion = 16844304; // 0x1010610
field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
- field public static final int windowBackgroundBlurEnabled = 16844316; // 0x101061c
- field public static final int windowBackgroundBlurRadius = 16844315; // 0x101061b
}
public static final class R.bool {
@@ -631,8 +629,6 @@
method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long);
method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(int, long);
method public android.os.Bundle toBundle();
- field public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
- field public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
}
public class DownloadManager {
@@ -1493,6 +1489,169 @@
}
+package android.app.smartspace {
+
+ public final class SmartspaceAction implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public CharSequence getContentDescription();
+ method @Nullable public android.os.Bundle getExtras();
+ method @Nullable public android.graphics.drawable.Icon getIcon();
+ method @NonNull public String getId();
+ method @Nullable public android.content.Intent getIntent();
+ method @Nullable public android.app.PendingIntent getPendingIntent();
+ method @Nullable public CharSequence getSubtitle();
+ method @NonNull public CharSequence getTitle();
+ method @Nullable public android.os.UserHandle getUserHandle();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceAction> CREATOR;
+ }
+
+ public static final class SmartspaceAction.Builder {
+ ctor public SmartspaceAction.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceAction build();
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setContentDescription(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setIcon(@Nullable android.graphics.drawable.Icon);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setIntent(@Nullable android.content.Intent);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setPendingIntent(@Nullable android.app.PendingIntent);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setSubtitle(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setUserHandle(@Nullable android.os.UserHandle);
+ }
+
+ public final class SmartspaceConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public String getPackageName();
+ method @NonNull public int getSmartspaceTargetCount();
+ method @NonNull public String getUiSurface();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceConfig> CREATOR;
+ }
+
+ public static final class SmartspaceConfig.Builder {
+ ctor public SmartspaceConfig.Builder(@NonNull android.content.Context, @NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceConfig build();
+ method @NonNull public android.app.smartspace.SmartspaceConfig.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.app.smartspace.SmartspaceConfig.Builder setSmartspaceTargetCount(int);
+ }
+
+ public final class SmartspaceManager {
+ method @NonNull public android.app.smartspace.SmartspaceSession createSmartspaceSession(@NonNull android.app.smartspace.SmartspaceConfig);
+ }
+
+ public final class SmartspaceSession implements java.lang.AutoCloseable {
+ method public void close();
+ method public void destroy();
+ method protected void finalize();
+ method public void notifySmartspaceEvent(@NonNull android.app.smartspace.SmartspaceTargetEvent);
+ method public void registerSmartspaceUpdates(@NonNull java.util.concurrent.Executor, @NonNull android.app.smartspace.SmartspaceSession.Callback);
+ method public void requestSmartspaceUpdate();
+ method public void unregisterSmartspaceUpdates(@NonNull android.app.smartspace.SmartspaceSession.Callback);
+ }
+
+ public static interface SmartspaceSession.Callback {
+ method public void onTargetsAvailable(@NonNull java.util.List<android.app.smartspace.SmartspaceTarget>);
+ }
+
+ public final class SmartspaceSessionId implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getId();
+ method @NonNull public int getUserId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceSessionId> CREATOR;
+ }
+
+ public final class SmartspaceTarget implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.app.smartspace.SmartspaceAction> getActionChips();
+ method @Nullable public String getAssociatedSmartspaceTargetId();
+ method @Nullable public android.app.smartspace.SmartspaceAction getBaseAction();
+ method @NonNull public android.content.ComponentName getComponentName();
+ method @NonNull public long getCreationTimeMillis();
+ method @NonNull public long getExpiryTimeMillis();
+ method @NonNull public int getFeatureType();
+ method @Nullable public android.app.smartspace.SmartspaceAction getHeaderAction();
+ method @NonNull public java.util.List<android.app.smartspace.SmartspaceAction> getIconGrid();
+ method @NonNull public float getScore();
+ method @Nullable public android.net.Uri getSliceUri();
+ method @NonNull public String getSmartspaceTargetId();
+ method @Nullable public String getSourceNotificationKey();
+ method @NonNull public android.os.UserHandle getUserHandle();
+ method @Nullable public android.appwidget.AppWidgetProviderInfo getWidgetId();
+ method @NonNull public boolean isSensitive();
+ method @NonNull public boolean shouldShowExpanded();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceTarget> CREATOR;
+ field public static final int FEATURE_ALARM = 7; // 0x7
+ field public static final int FEATURE_BEDTIME_ROUTINE = 16; // 0x10
+ field public static final int FEATURE_CALENDAR = 2; // 0x2
+ field public static final int FEATURE_COMMUTE_TIME = 3; // 0x3
+ field public static final int FEATURE_CONSENT = 11; // 0xb
+ field public static final int FEATURE_ETA_MONITORING = 18; // 0x12
+ field public static final int FEATURE_FITNESS_TRACKING = 17; // 0x11
+ field public static final int FEATURE_FLIGHT = 4; // 0x4
+ field public static final int FEATURE_LOYALTY_CARD = 14; // 0xe
+ field public static final int FEATURE_MEDIA = 15; // 0xf
+ field public static final int FEATURE_MISSED_CALL = 19; // 0x13
+ field public static final int FEATURE_ONBOARDING = 8; // 0x8
+ field public static final int FEATURE_PACKAGE_TRACKING = 20; // 0x14
+ field public static final int FEATURE_REMINDER = 6; // 0x6
+ field public static final int FEATURE_SHOPPING_LIST = 13; // 0xd
+ field public static final int FEATURE_SPORTS = 9; // 0x9
+ field public static final int FEATURE_STOCK_PRICE_CHANGE = 12; // 0xc
+ field public static final int FEATURE_STOPWATCH = 22; // 0x16
+ field public static final int FEATURE_TIMER = 21; // 0x15
+ field public static final int FEATURE_TIPS = 5; // 0x5
+ field public static final int FEATURE_UNDEFINED = 0; // 0x0
+ field public static final int FEATURE_UPCOMING_ALARM = 23; // 0x17
+ field public static final int FEATURE_WEATHER = 1; // 0x1
+ field public static final int FEATURE_WEATHER_ALERT = 10; // 0xa
+ }
+
+ public static final class SmartspaceTarget.Builder {
+ ctor public SmartspaceTarget.Builder(@NonNull String, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
+ method @NonNull public android.app.smartspace.SmartspaceTarget build();
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setActionChips(@NonNull java.util.List<android.app.smartspace.SmartspaceAction>);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setAssociatedSmartspaceTargetId(@NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setBaseAction(@NonNull android.app.smartspace.SmartspaceAction);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setCreationTimeMillis(@NonNull long);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setExpiryTimeMillis(@NonNull long);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setFeatureType(@NonNull int);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setHeaderAction(@NonNull android.app.smartspace.SmartspaceAction);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setIconGrid(@NonNull java.util.List<android.app.smartspace.SmartspaceAction>);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setScore(@NonNull float);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSensitive(@NonNull boolean);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setShouldShowExpanded(@NonNull boolean);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSliceUri(@NonNull android.net.Uri);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSourceNotificationKey(@NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setWidgetId(@NonNull android.appwidget.AppWidgetProviderInfo);
+ }
+
+ public final class SmartspaceTargetEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public int getEventType();
+ method @Nullable public String getSmartspaceActionId();
+ method @Nullable public android.app.smartspace.SmartspaceTarget getSmartspaceTarget();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceTargetEvent> CREATOR;
+ field public static final int EVENT_TARGET_BLOCK = 5; // 0x5
+ field public static final int EVENT_TARGET_DISMISS = 4; // 0x4
+ field public static final int EVENT_TARGET_INTERACTION = 1; // 0x1
+ field public static final int EVENT_TARGET_IN_VIEW = 2; // 0x2
+ field public static final int EVENT_TARGET_OUT_OF_VIEW = 3; // 0x3
+ field public static final int EVENT_UI_SURFACE_IN_VIEW = 6; // 0x6
+ field public static final int EVENT_UI_SURFACE_OUT_OF_VIEW = 7; // 0x7
+ }
+
+ public static final class SmartspaceTargetEvent.Builder {
+ ctor public SmartspaceTargetEvent.Builder(int);
+ method @NonNull public android.app.smartspace.SmartspaceTargetEvent build();
+ method @NonNull public android.app.smartspace.SmartspaceTargetEvent.Builder setSmartspaceActionId(@NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceTargetEvent.Builder setSmartspaceTarget(@NonNull android.app.smartspace.SmartspaceTarget);
+ }
+
+}
+
package android.app.time {
public final class TimeManager {
@@ -1628,8 +1787,10 @@
package android.apphibernation {
public final class AppHibernationManager {
- method public boolean isHibernating(@NonNull String);
- method public void setHibernating(@NonNull String, boolean);
+ method public boolean isHibernatingForUser(@NonNull String);
+ method public boolean isHibernatingGlobally(@NonNull String);
+ method public void setHibernatingForUser(@NonNull String, boolean);
+ method public void setHibernatingGlobally(@NonNull String, boolean);
}
}
@@ -1953,6 +2114,7 @@
field public static final String ROLLBACK_SERVICE = "rollback";
field public static final String SEARCH_UI_SERVICE = "search_ui";
field public static final String SECURE_ELEMENT_SERVICE = "secure_element";
+ field public static final String SMARTSPACE_SERVICE = "smartspace";
field public static final String STATS_MANAGER = "stats";
field public static final String STATUS_BAR_SERVICE = "statusbar";
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
@@ -2356,6 +2518,7 @@
field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
+ field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
field public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = -268435456; // 0xf0000000
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000
@@ -2592,22 +2755,21 @@
}
-package android.graphics.drawable {
-
- public final class BackgroundBlurDrawable extends android.graphics.drawable.Drawable {
- ctor @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public BackgroundBlurDrawable();
- method public void setBlurRadius(int);
- method public void setColor(@ColorInt int);
- method public void setCornerRadius(float);
- method public void setCornerRadius(float, float, float, float);
- }
-
-}
-
package android.graphics.fonts {
public class FontManager {
method @Nullable public android.text.FontConfig getFontConfig();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int);
+ field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb
+ field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff
+ field public static final int RESULT_ERROR_FAILED_UPDATE_CONFIG = -6; // 0xfffffffa
+ field public static final int RESULT_ERROR_FONT_UPDATER_DISABLED = -7; // 0xfffffff9
+ field public static final int RESULT_ERROR_INVALID_FONT_FILE = -3; // 0xfffffffd
+ field public static final int RESULT_ERROR_INVALID_FONT_NAME = -4; // 0xfffffffc
+ field public static final int RESULT_ERROR_REMOTE_EXCEPTION = -9; // 0xfffffff7
+ field public static final int RESULT_ERROR_VERIFICATION_FAILURE = -2; // 0xfffffffe
+ field public static final int RESULT_ERROR_VERSION_MISMATCH = -8; // 0xfffffff8
+ field public static final int RESULT_SUCCESS = 0; // 0x0
}
}
@@ -2697,6 +2859,9 @@
field public final boolean nightMode;
field public final String packageName;
field public final float powerBrightnessFactor;
+ field public final boolean reduceBrightColors;
+ field public final float reduceBrightColorsOffset;
+ field public final int reduceBrightColorsStrength;
field public final long timeStamp;
}
@@ -3170,6 +3335,7 @@
public class ContextHubClientCallback {
ctor public ContextHubClientCallback();
+ method public void onClientAuthorizationChanged(@NonNull android.hardware.location.ContextHubClient, long, int);
method public void onHubReset(android.hardware.location.ContextHubClient);
method public void onMessageFromNanoApp(android.hardware.location.ContextHubClient, android.hardware.location.NanoAppMessage);
method public void onNanoAppAborted(android.hardware.location.ContextHubClient, long, int);
@@ -3206,6 +3372,7 @@
public class ContextHubIntentEvent {
method @NonNull public static android.hardware.location.ContextHubIntentEvent fromIntent(@NonNull android.content.Intent);
+ method public int getClientAuthorizationState();
method @NonNull public android.hardware.location.ContextHubInfo getContextHubInfo();
method public int getEventType();
method public int getNanoAppAbortCode();
@@ -3214,8 +3381,10 @@
}
public final class ContextHubManager {
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.location.ContextHubClientCallback);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback, @NonNull java.util.concurrent.Executor);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
@@ -3233,6 +3402,10 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int unloadNanoApp(int);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
+ field public static final int AUTHORIZATION_DENIED = 0; // 0x0
+ field public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; // 0x1
+ field public static final int AUTHORIZATION_GRANTED = 2; // 0x2
+ field public static final int EVENT_CLIENT_AUTHORIZATION = 7; // 0x7
field public static final int EVENT_HUB_RESET = 6; // 0x6
field public static final int EVENT_NANOAPP_ABORTED = 4; // 0x4
field public static final int EVENT_NANOAPP_DISABLED = 3; // 0x3
@@ -3240,6 +3413,7 @@
field public static final int EVENT_NANOAPP_LOADED = 0; // 0x0
field public static final int EVENT_NANOAPP_MESSAGE = 5; // 0x5
field public static final int EVENT_NANOAPP_UNLOADED = 1; // 0x1
+ field public static final String EXTRA_CLIENT_AUTHORIZATION_STATE = "android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE";
field public static final String EXTRA_CONTEXT_HUB_INFO = "android.hardware.location.extra.CONTEXT_HUB_INFO";
field public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
field public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
@@ -3478,8 +3652,10 @@
public final class NanoAppState implements android.os.Parcelable {
ctor public NanoAppState(long, int, boolean);
+ ctor public NanoAppState(long, int, boolean, @NonNull java.util.List<java.lang.String>);
method public int describeContents();
method public long getNanoAppId();
+ method @NonNull public java.util.List<java.lang.String> getNanoAppPermissions();
method public long getNanoAppVersion();
method public boolean isEnabled();
method public void writeToParcel(android.os.Parcel, int);
@@ -4622,6 +4798,7 @@
public static class AudioAttributes.Builder {
method public android.media.AudioAttributes.Builder addBundle(@NonNull android.os.Bundle);
method public android.media.AudioAttributes.Builder setCapturePreset(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public android.media.AudioAttributes.Builder setHotwordMode();
method public android.media.AudioAttributes.Builder setInternalCapturePreset(int);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int);
}
@@ -4763,7 +4940,7 @@
}
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
- ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
+ ctor @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
}
public static class AudioRecord.Builder {
@@ -5804,6 +5981,7 @@
public final class RestartEvent extends android.media.tv.tuner.filter.FilterEvent {
method public int getStartId();
+ field public static final int NEW_FILTER_FIRST_START_ID = 0; // 0x0
}
public final class ScramblingStatusEvent extends android.media.tv.tuner.filter.FilterEvent {
@@ -6938,11 +7116,15 @@
}
public final class IpSecManager {
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
}
public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ method public void close();
method @NonNull public String getInterfaceName();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
}
public static class IpSecTransform.Builder {
@@ -7087,6 +7269,7 @@
method @Nullable public String getSsid();
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+ field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
@@ -8281,6 +8464,8 @@
field public static final int EVENT_MMS = 2; // 0x2
field public static final int EVENT_SMS = 1; // 0x1
field public static final int EVENT_UNSPECIFIED = 0; // 0x0
+ field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
+ field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
}
public class RecoverySystem {
@@ -8883,6 +9068,7 @@
field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
field public static final String NAMESPACE_SYSTEMUI = "systemui";
+ field public static final String NAMESPACE_SYSTEM_TIME = "system_time";
field public static final String NAMESPACE_TELEPHONY = "telephony";
field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
field public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
@@ -9130,6 +9316,24 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
}
+ public final class SimPhonebookContract {
+ method @NonNull public static String getEfUriPath(int);
+ field public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+ }
+
+ public static final class SimPhonebookContract.ElementaryFiles {
+ field public static final String EF_ADN_PATH_SEGMENT = "adn";
+ field public static final String EF_FDN_PATH_SEGMENT = "fdn";
+ field public static final String EF_SDN_PATH_SEGMENT = "sdn";
+ field public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+ }
+
+ public static final class SimPhonebookContract.SimRecords {
+ field public static final String EXTRA_NAME_VALIDATION_RESULT = "android.provider.extra.NAME_VALIDATION_RESULT";
+ field public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+ field public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+ }
+
public static final class Telephony.Carriers implements android.provider.BaseColumns {
field public static final String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
@@ -9422,29 +9626,6 @@
}
-package android.service.attestation {
-
- public abstract class ImpressionAttestationService extends android.app.Service {
- ctor public ImpressionAttestationService();
- method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
- method public abstract boolean onVerifyImpressionToken(@NonNull byte[], @NonNull android.service.attestation.ImpressionToken);
- }
-
- public final class ImpressionToken implements android.os.Parcelable {
- ctor public ImpressionToken(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
- method public int describeContents();
- method @NonNull public android.graphics.Rect getBoundsInWindow();
- method @NonNull public String getHashingAlgorithm();
- method @NonNull public byte[] getHmac();
- method @NonNull public byte[] getImageHash();
- method public long getScreenshotTimeMillis();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.attestation.ImpressionToken> CREATOR;
- }
-
-}
-
package android.service.autofill {
public abstract class AutofillFieldClassificationService extends android.app.Service {
@@ -9832,6 +10013,7 @@
method public void onNotificationDirectReplied(@NonNull String);
method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification);
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
+ method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel, @NonNull android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String);
method public void onNotificationVisibilityChanged(@NonNull String, boolean);
@@ -10009,6 +10191,30 @@
}
+package android.service.screenshot {
+
+ public final class ScreenshotHash implements android.os.Parcelable {
+ ctor public ScreenshotHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
+ method public int describeContents();
+ method @NonNull public android.graphics.Rect getBoundsInWindow();
+ method @NonNull public String getHashingAlgorithm();
+ method @NonNull public byte[] getHmac();
+ method @NonNull public byte[] getImageHash();
+ method public long getScreenshotTimeMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.screenshot.ScreenshotHash> CREATOR;
+ }
+
+ public abstract class ScreenshotHasherService extends android.app.Service {
+ ctor public ScreenshotHasherService();
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @Nullable public abstract android.service.screenshot.ScreenshotHash onGenerateScreenshotHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
+ method public abstract boolean onVerifyScreenshotHash(@NonNull byte[], @NonNull android.service.screenshot.ScreenshotHash);
+ field public static final String SERVICE_INTERFACE = "android.service.screenshot.ScreenshotHasherService";
+ }
+
+}
+
package android.service.search {
public abstract class SearchUiService extends android.app.Service {
@@ -10057,6 +10263,21 @@
}
+package android.service.smartspace {
+
+ public abstract class SmartspaceService extends android.app.Service {
+ ctor public SmartspaceService();
+ method @MainThread public abstract void notifySmartspaceEvent(@NonNull android.app.smartspace.SmartspaceSessionId, @NonNull android.app.smartspace.SmartspaceTargetEvent);
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onCreateSmartspaceSession(@NonNull android.app.smartspace.SmartspaceConfig, @NonNull android.app.smartspace.SmartspaceSessionId);
+ method @MainThread public abstract void onDestroy(@NonNull android.app.smartspace.SmartspaceSessionId);
+ method public abstract void onDestroySmartspaceSession(@NonNull android.app.smartspace.SmartspaceSessionId);
+ method @MainThread public abstract void onRequestSmartspaceUpdate(@NonNull android.app.smartspace.SmartspaceSessionId);
+ method public final void updateSmartspaceTargets(@NonNull android.app.smartspace.SmartspaceSessionId, @NonNull java.util.List<android.app.smartspace.SmartspaceTarget>);
+ }
+
+}
+
package android.service.storage {
public abstract class ExternalStorageService extends android.app.Service {
@@ -11608,7 +11829,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method public int setNrDualConnectivityState(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
@@ -13141,6 +13362,7 @@
field public static final String IPTYPE_IPV6 = "IPV6";
field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+ field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
@@ -13173,6 +13395,7 @@
field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+ field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
field public static final String SIP_TRANSPORT_TCP = "TCP";
field public static final String SIP_TRANSPORT_UDP = "UDP";
}
@@ -13660,7 +13883,9 @@
public final class FontConfig implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.text.FontConfig.Alias> getAliases();
+ method @IntRange(from=0) public int getConfigVersion();
method @NonNull public java.util.List<android.text.FontConfig.FontFamily> getFontFamilies();
+ method public long getLastModifiedTimeMillis();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
}
@@ -13885,10 +14110,8 @@
method public boolean isSystemApplicationOverlay();
method @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public void setSystemApplicationOverlay(boolean);
method public final void setUserActivityTimeout(long);
- field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public static final int FLAG_BLUR_BEHIND = 4; // 0x4
field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10
- field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public int backgroundBlurRadius;
}
@IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
@@ -14158,6 +14381,7 @@
method public String getDataDirectorySuffix();
method public String getErrorString(android.content.Context, int);
method public int getPackageId(android.content.res.Resources, String);
+ method @NonNull public long[] getTimestamps();
method @Deprecated public void invokeDrawGlFunctor(android.view.View, long, boolean);
method public boolean isMultiProcessEnabled();
method public boolean isTraceTagEnabled();
@@ -14173,6 +14397,12 @@
method public static android.content.pm.PackageInfo getLoadedPackageInfo();
method public static int loadWebViewNativeLibraryFromPackage(String, ClassLoader);
method public static void prepareWebViewInZygote();
+ field public static final int ADD_ASSETS_END = 4; // 0x4
+ field public static final int ADD_ASSETS_START = 3; // 0x3
+ field public static final int CREATE_CONTEXT_END = 2; // 0x2
+ field public static final int CREATE_CONTEXT_START = 1; // 0x1
+ field public static final int GET_CLASS_LOADER_END = 6; // 0x6
+ field public static final int GET_CLASS_LOADER_START = 5; // 0x5
field public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; // 0x2
field public static final int LIBLOAD_FAILED_JNI_CALL = 7; // 0x7
field public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; // 0x4
@@ -14183,6 +14413,11 @@
field public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8; // 0x8
field public static final int LIBLOAD_SUCCESS = 0; // 0x0
field public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; // 0x1
+ field public static final int NATIVE_LOAD_END = 8; // 0x8
+ field public static final int NATIVE_LOAD_START = 7; // 0x7
+ field public static final int PROVIDER_CLASS_FOR_NAME_END = 10; // 0xa
+ field public static final int PROVIDER_CLASS_FOR_NAME_START = 9; // 0x9
+ field public static final int WEBVIEW_LOAD_START = 0; // 0x0
}
public interface WebViewFactoryProvider {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 8b3cee4..58f6c52 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -6,9 +6,11 @@
BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex):
Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex)
+
ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
Registration methods should have overload that accepts delivery Executor: `setOnRtpRxNoticeListener`
-
+
+
GenericException: android.app.prediction.AppPredictor#finalize():
GenericException: android.hardware.location.ContextHubClient#finalize():
@@ -21,6 +23,8 @@
IntentBuilderName: android.app.search.SearchAction#getIntent():
+IntentBuilderName: android.app.smartspace.SmartspaceAction#getIntent():
+ Methods creating an Intent should be named `create<Foo>Intent()`, was `getIntent`
KotlinKeyword: android.app.Notification#when:
@@ -83,6 +87,10 @@
+OnNameExpected: android.service.smartspace.SmartspaceService#notifySmartspaceEvent(android.app.smartspace.SmartspaceSessionId, android.app.smartspace.SmartspaceTargetEvent):
+ Methods implemented by developers should follow the on<Something> style, was `notifySmartspaceEvent`
+
+
ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
@@ -187,11 +195,10 @@
SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
-SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
- SAM-compatible parameters (such as parameter 2, "listener", in android.media.MediaPlayer.setOnRtpRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-
SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 2, "listener", in android.media.MediaPlayer.setOnRtpRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
SamShouldBeLast: android.media.MediaRecorder#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback):
@@ -258,3 +265,5 @@
Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle`
UserHandleName: android.app.search.SearchTarget.Builder#setUserHandle(android.os.UserHandle):
+UserHandleName: android.app.smartspace.SmartspaceAction.Builder#setUserHandle(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle`
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ea7dc03..eda88e9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -281,6 +281,7 @@
}
public final class PendingIntent implements android.os.Parcelable {
+ method @Nullable @RequiresPermission("android.permission.GET_INTENT_SENDER_INTENT") public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
field @Deprecated public static final int FLAG_MUTABLE_UNAUDITED = 33554432; // 0x2000000
}
@@ -388,7 +389,8 @@
method public boolean isFactoryResetProtectionPolicySupported();
method @NonNull public static String operationToString(int);
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
- method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, boolean);
+ method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
+ method @NonNull public static String unsafeOperationReasonToString(int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED";
field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE";
@@ -400,10 +402,10 @@
field public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
field public static final int CODE_NONSYSTEM_USER_EXISTS = 5; // 0x5
field public static final int CODE_NOT_SYSTEM_USER = 7; // 0x7
- field public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12; // 0xc
+ field @Deprecated public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12; // 0xc
field public static final int CODE_OK = 0; // 0x0
field public static final int CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15; // 0xf
- field public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
+ field @Deprecated public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
field public static final int CODE_SYSTEM_USER = 10; // 0xa
field public static final int CODE_USER_HAS_PROFILE_OWNER = 2; // 0x2
field public static final int CODE_USER_NOT_RUNNING = 3; // 0x3
@@ -455,6 +457,7 @@
field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
+ field public static final int UNSAFE_OPERATION_REASON_NONE = -1; // 0xffffffff
}
public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
@@ -511,6 +514,7 @@
}
public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
+ ctor public UnsafeStateException(int, int);
method public int getOperation();
}
@@ -825,6 +829,17 @@
public class FontManager {
method @Nullable public android.text.FontConfig getFontConfig();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int);
+ field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb
+ field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff
+ field public static final int RESULT_ERROR_FAILED_UPDATE_CONFIG = -6; // 0xfffffffa
+ field public static final int RESULT_ERROR_FONT_UPDATER_DISABLED = -7; // 0xfffffff9
+ field public static final int RESULT_ERROR_INVALID_FONT_FILE = -3; // 0xfffffffd
+ field public static final int RESULT_ERROR_INVALID_FONT_NAME = -4; // 0xfffffffc
+ field public static final int RESULT_ERROR_REMOTE_EXCEPTION = -9; // 0xfffffff7
+ field public static final int RESULT_ERROR_VERIFICATION_FAILURE = -2; // 0xfffffffe
+ field public static final int RESULT_ERROR_VERSION_MISMATCH = -8; // 0xfffffff8
+ field public static final int RESULT_SUCCESS = 0; // 0x0
}
}
@@ -932,9 +947,8 @@
public final class InputManager {
method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context);
- method public float getMaximumObscuringOpacityForTouch(@NonNull android.content.Context);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, float);
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(float);
field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
}
@@ -2032,7 +2046,9 @@
public final class FontConfig implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.text.FontConfig.Alias> getAliases();
+ method @IntRange(from=0) public int getConfigVersion();
method @NonNull public java.util.List<android.text.FontConfig.FontFamily> getFontFamilies();
+ method public long getLastModifiedTimeMillis();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
}
@@ -2311,6 +2327,8 @@
}
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
+ method @Nullable public final android.os.IBinder getWindowContextToken();
+ method public final void setWindowContextToken(@NonNull android.os.IBinder);
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
field public CharSequence accessibilityTitle;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bdd541a..4728f11 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5090,6 +5090,13 @@
mTaskDescription.setBackgroundColor(colorBackground);
}
+ int colorBackgroundFloating = a.getColor(
+ com.android.internal.R.styleable.ActivityTaskDescription_colorBackgroundFloating,
+ 0);
+ if (colorBackgroundFloating != 0 && Color.alpha(colorBackgroundFloating) == 0xFF) {
+ mTaskDescription.setBackgroundColorFloating(colorBackgroundFloating);
+ }
+
final int statusBarColor = a.getColor(
com.android.internal.R.styleable.ActivityTaskDescription_statusBarColor, 0);
if (statusBarColor != 0) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 520959c..43d0269 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1072,13 +1072,15 @@
private static final String ATTR_TASKDESCRIPTIONCOLOR_PRIMARY =
ATTR_TASKDESCRIPTION_PREFIX + "color";
private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND =
- ATTR_TASKDESCRIPTION_PREFIX + "colorBackground";
+ ATTR_TASKDESCRIPTION_PREFIX + "color_background";
private static final String ATTR_TASKDESCRIPTIONICON_FILENAME =
ATTR_TASKDESCRIPTION_PREFIX + "icon_filename";
private static final String ATTR_TASKDESCRIPTIONICON_RESOURCE =
ATTR_TASKDESCRIPTION_PREFIX + "icon_resource";
private static final String ATTR_TASKDESCRIPTIONICON_RESOURCE_PACKAGE =
ATTR_TASKDESCRIPTION_PREFIX + "icon_package";
+ private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND_FLOATING =
+ ATTR_TASKDESCRIPTION_PREFIX + "color_background_floating";
private String mLabel;
@Nullable
@@ -1086,6 +1088,7 @@
private String mIconFilename;
private int mColorPrimary;
private int mColorBackground;
+ private int mColorBackgroundFloating;
private int mStatusBarColor;
private int mNavigationBarColor;
private boolean mEnsureStatusBarContrastWhenTransparent;
@@ -1106,7 +1109,7 @@
*/
public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) {
this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes),
- colorPrimary, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ colorPrimary, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1121,7 +1124,7 @@
*/
public TaskDescription(String label, @DrawableRes int iconRes) {
this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes),
- 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/**
@@ -1130,14 +1133,14 @@
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
- this(label, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ this(label, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
- this(null, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ this(null, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/**
@@ -1152,7 +1155,7 @@
@Deprecated
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
this(label, icon != null ? Icon.createWithBitmap(icon) : null, colorPrimary, 0, 0, 0,
- false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1168,7 +1171,7 @@
@Deprecated
public TaskDescription(String label, Bitmap icon) {
this(label, icon != null ? Icon.createWithBitmap(icon) : null, 0, 0, 0, 0, false, false,
- RESIZE_MODE_RESIZEABLE, -1, -1);
+ RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/** @hide */
@@ -1177,7 +1180,7 @@
int statusBarColor, int navigationBarColor,
boolean ensureStatusBarContrastWhenTransparent,
boolean ensureNavigationBarContrastWhenTransparent, int resizeMode, int minWidth,
- int minHeight) {
+ int minHeight, int colorBackgroundFloating) {
mLabel = label;
mIcon = icon;
mColorPrimary = colorPrimary;
@@ -1190,6 +1193,7 @@
mResizeMode = resizeMode;
mMinWidth = minWidth;
mMinHeight = minHeight;
+ mColorBackgroundFloating = colorBackgroundFloating;
}
/**
@@ -1217,6 +1221,7 @@
mResizeMode = other.mResizeMode;
mMinWidth = other.mMinWidth;
mMinHeight = other.mMinHeight;
+ mColorBackgroundFloating = other.mColorBackgroundFloating;
}
/**
@@ -1253,6 +1258,9 @@
if (other.mMinHeight != -1) {
mMinHeight = other.mMinHeight;
}
+ if (other.mColorBackgroundFloating != 0) {
+ mColorBackgroundFloating = other.mColorBackgroundFloating;
+ }
}
private TaskDescription(Parcel source) {
@@ -1292,6 +1300,19 @@
}
/**
+ * Sets the background color floating for this task description.
+ * @hide
+ */
+ public void setBackgroundColorFloating(int backgroundColor) {
+ // Ensure that the given color is valid
+ if ((backgroundColor != 0) && (Color.alpha(backgroundColor) != 255)) {
+ throw new RuntimeException(
+ "A TaskDescription's background color floating should be opaque");
+ }
+ mColorBackgroundFloating = backgroundColor;
+ }
+
+ /**
* @hide
*/
public void setStatusBarColor(int statusBarColor) {
@@ -1461,6 +1482,14 @@
}
/**
+ * @return The background color floating.
+ * @hide
+ */
+ public int getBackgroundColorFloating() {
+ return mColorBackgroundFloating;
+ }
+
+ /**
* @hide
*/
public int getStatusBarColor() {
@@ -1537,6 +1566,10 @@
if (mColorBackground != 0) {
out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, mColorBackground);
}
+ if (mColorBackgroundFloating != 0) {
+ out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND_FLOATING,
+ mColorBackgroundFloating);
+ }
if (mIconFilename != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONICON_FILENAME, mIconFilename);
}
@@ -1563,6 +1596,11 @@
if (colorBackground != 0) {
setBackgroundColor(colorBackground);
}
+ final int colorBackgroundFloating = in.getAttributeIntHex(null,
+ ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND_FLOATING, 0);
+ if (colorBackgroundFloating != 0) {
+ setBackgroundColorFloating(colorBackgroundFloating);
+ }
final String iconFilename = in.getAttributeValue(null,
ATTR_TASKDESCRIPTIONICON_FILENAME);
if (iconFilename != null) {
@@ -1615,6 +1653,7 @@
dest.writeInt(1);
dest.writeString(mIconFilename);
}
+ dest.writeInt(mColorBackgroundFloating);
}
public void readFromParcel(Parcel source) {
@@ -1632,6 +1671,7 @@
mMinWidth = source.readInt();
mMinHeight = source.readInt();
mIconFilename = source.readInt() > 0 ? source.readString() : null;
+ mColorBackgroundFloating = source.readInt();
}
public static final @android.annotation.NonNull Creator<TaskDescription> CREATOR
@@ -1655,7 +1695,8 @@
+ (mEnsureNavigationBarContrastWhenTransparent
? " (contrast when transparent)" : "")
+ " resizeMode: " + ActivityInfo.resizeModeToString(mResizeMode)
- + " minWidth: " + mMinWidth + " minHeight: " + mMinHeight;
+ + " minWidth: " + mMinWidth + " minHeight: " + mMinHeight
+ + " colorBackgrounFloating: " + mColorBackgroundFloating;
}
@Override
@@ -1678,7 +1719,8 @@
== other.mEnsureNavigationBarContrastWhenTransparent
&& mResizeMode == other.mResizeMode
&& mMinWidth == other.mMinWidth
- && mMinHeight == other.mMinHeight;
+ && mMinHeight == other.mMinHeight
+ && mColorBackgroundFloating == other.mColorBackgroundFloating;
}
/** @hide */
@@ -1826,6 +1868,8 @@
pw.print(ActivityInfo.resizeModeToString(td.getResizeMode()));
pw.print(" minWidth="); pw.print(td.getMinWidth());
pw.print(" minHeight="); pw.print(td.getMinHeight());
+ pw.print(" colorBackgroundFloating=#");
+ pw.print(Integer.toHexString(td.getBackgroundColorFloating()));
pw.println(" }");
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7402690..9b6f4b4 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -30,6 +30,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
import android.util.ArraySet;
@@ -103,7 +104,7 @@
* @param target
* @param whitelistToken
* @param duration temp allowlist duration in milliseconds.
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param type temp allowlist type defined at {@link TempAllowListType}
*/
public abstract void setPendingIntentWhitelistDuration(IIntentSender target,
IBinder whitelistToken, long duration, int type);
@@ -136,10 +137,10 @@
* @param changingUid uid to add or remove to temp allowlist.
* @param adding true to add to temp allowlist, false to remove from temp allowlist.
* @param durationMs when adding is true, the duration to be in temp allowlist.
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}.
+ * @param type temp allowlist type defined at {@link TempAllowListType}.
*/
public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid,
- boolean adding, long durationMs, @BroadcastOptions.TempAllowListType int type);
+ boolean adding, long durationMs, @TempAllowListType int type);
/**
* Get the procstate for the UID. The return value will be between
@@ -333,7 +334,7 @@
* @param callerUid the UID that sent the PendingIntent.
* @param targetUid the UID that is been temp allowlisted.
* @param duration temp allowlist duration in milliseconds.
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param type temp allowlist type defined at {@link TempAllowListType}
* @param tag
*/
public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9b141b7..e5a04c9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -95,7 +95,6 @@
import android.media.MediaFrameworkPlatformInitializer;
import android.media.MediaServiceManager;
import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
import android.net.Proxy;
import android.net.Uri;
import android.os.AsyncTask;
@@ -2291,9 +2290,10 @@
* Resources if one has already been created.
*/
Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
- String[] libDirs, LoadedApk pkgInfo) {
+ String[] libDirs, LoadedApk pkgInfo, Configuration overrideConfig) {
return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs,
- null, null, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(), null);
+ null, overrideConfig, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(),
+ null);
}
@UnsupportedAppUsage
@@ -6575,25 +6575,6 @@
// Pass the current context to HardwareRenderer
HardwareRenderer.setContextForInit(getSystemContext());
- /**
- * Initialize the default http proxy in this process for the reasons we set the time zone.
- */
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Setup proxies");
- final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- if (b != null) {
- // In pre-boot mode (doing initial launch to collect password), not
- // all system is up. This includes the connectivity service, so don't
- // crash if we can't get it.
- final IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- try {
- Proxy.setHttpProxySystemProperty(service.getProxyForNetwork(null));
- } catch (RemoteException e) {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- throw e.rethrowFromSystemServer();
- }
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
// Instrumentation info affects the class loader, so load it before
// setting up the app context.
final InstrumentationInfo ii;
@@ -6607,6 +6588,23 @@
updateLocaleListFromAppContext(appContext,
mResourcesManager.getConfiguration().getLocales());
+ // Initialize the default http proxy in this process.
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Setup proxies");
+ try {
+ // In pre-boot mode (doing initial launch to collect password), not all system is up.
+ // This includes the connectivity service, so trying to obtain ConnectivityManager at
+ // that point would return null. Check whether the ConnectivityService is available, and
+ // avoid crashing with a NullPointerException if it is not.
+ final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ if (b != null) {
+ final ConnectivityManager cm =
+ appContext.getSystemService(ConnectivityManager.class);
+ Proxy.setHttpProxyConfiguration(cm.getDefaultProxy());
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
if (!Process.isIsolated()) {
final int oldMask = StrictMode.allowThreadDiskWritesMask();
try {
@@ -7521,8 +7519,8 @@
}
public static void updateHttpProxy(@NonNull Context context) {
- final ConnectivityManager cm = ConnectivityManager.from(context);
- Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
+ final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+ Proxy.setHttpProxyConfiguration(cm.getDefaultProxy());
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index d716a3c..7410a1c 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -50,7 +50,7 @@
*
* <p>
* Application process could die for many reasons, for example {@link #REASON_LOW_MEMORY}
- * when it was killed by the ystem because it was running low on memory. Reason
+ * when it was killed by the system because it was running low on memory. Reason
* of the death can be retrieved via {@link #getReason}. Besides the reason, there are a few other
* auxiliary APIs like {@link #getStatus} and {@link #getImportance} to help the caller with
* additional diagnostic information.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 77542bd..b51d4ac 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -59,6 +59,7 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -70,6 +71,13 @@
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
@@ -115,6 +123,7 @@
import libcore.util.EmptyArray;
+import java.io.File;
import java.lang.ref.WeakReference;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
@@ -1691,20 +1700,29 @@
@Override
public Resources getResourcesForApplication(@NonNull ApplicationInfo app)
throws NameNotFoundException {
+ return getResourcesForApplication(app, null);
+ }
+
+ @Override
+ public Resources getResourcesForApplication(@NonNull ApplicationInfo app,
+ @Nullable Configuration configuration) throws NameNotFoundException {
if (app.packageName.equals("system")) {
- return mContext.mMainThread.getSystemUiContext().getResources();
+ Context sysuiContext = mContext.mMainThread.getSystemUiContext();
+ if (configuration != null) {
+ sysuiContext = sysuiContext.createConfigurationContext(configuration);
+ }
+ return sysuiContext.getResources();
}
final boolean sameUid = (app.uid == Process.myUid());
final Resources r = mContext.mMainThread.getTopLevelResources(
- sameUid ? app.sourceDir : app.publicSourceDir,
- sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
- app.resourceDirs, app.sharedLibraryFiles,
- mContext.mPackageInfo);
+ sameUid ? app.sourceDir : app.publicSourceDir,
+ sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
+ app.resourceDirs, app.sharedLibraryFiles,
+ mContext.mPackageInfo, configuration);
if (r != null) {
return r;
}
throw new NameNotFoundException("Unable to open " + app.publicSourceDir);
-
}
@Override
@@ -2045,6 +2063,31 @@
return info.loadLabel(this);
}
+ @Nullable
+ public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
+ @PackageInfoFlags int flags) {
+ if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
+ // Caller expressed no opinion about what encryption
+ // aware/unaware components they want to see, so match both
+ flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ }
+
+ boolean collectCertificates = (flags & PackageManager.GET_SIGNATURES) != 0
+ || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
+
+ ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
+ ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
+ new File(archiveFilePath), 0, getPermissionManager().getSplitPermissions(),
+ collectCertificates);
+ if (result.isError()) {
+ return null;
+ }
+ return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flags, 0, 0, null,
+ new PackageUserState(), UserHandle.getCallingUserId());
+ }
+
@Override
public int installExistingPackage(String packageName) throws NameNotFoundException {
return installExistingPackage(packageName, INSTALL_REASON_UNKNOWN);
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index a16f6a8..445fdd8 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -16,14 +16,12 @@
package android.app;
-import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.Build;
import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import android.os.PowerWhitelistManager;
+import android.os.PowerWhitelistManager.TempAllowListType;
/**
* Helper class for building an options Bundle that can be used with
@@ -75,25 +73,21 @@
"android:broadcast.allowBackgroundActivityStarts";
/**
- * Allow the temp allowlist behavior, plus allow foreground service start from background.
- */
- public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
- /**
- * Only allow the temp allowlist behavior, not allow foreground service start from
- * background.
- */
- public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
-
- /**
- * The list of temp allowlist types.
* @hide
+ * @deprecated Use {@link android.os.PowerWhitelistManager#
+ * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead.
*/
- @IntDef(flag = true, prefix = { "TEMPORARY_WHITELIST_TYPE_" }, value = {
- TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TempAllowListType {}
+ @Deprecated
+ public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
+ PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+ /**
+ * @hide
+ * @deprecated Use {@link android.os.PowerWhitelistManager#
+ * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} instead.
+ */
+ @Deprecated
+ public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
+ PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
public static BroadcastOptions makeBasic() {
BroadcastOptions opts = new BroadcastOptions();
@@ -125,7 +119,8 @@
android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
public void setTemporaryAppWhitelistDuration(long duration) {
mTemporaryAppWhitelistDuration = duration;
- mTemporaryAppWhitelistType = TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+ mTemporaryAppWhitelistType =
+ PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
}
/**
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
new file mode 100644
index 0000000..8b6570f
--- /dev/null
+++ b/core/java/android/app/GameManager.java
@@ -0,0 +1,98 @@
+/*
+ * 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.IntDef;
+import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The GameManager allows system apps to modify and query the game mode of apps.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@SystemService(Context.GAME_SERVICE)
+public final class GameManager {
+
+ private static final String TAG = "GameManager";
+
+ private final Context mContext;
+ private final IGameManagerService mService;
+
+ @IntDef(flag = false, prefix = { "GAME_MODE_" }, value = {
+ GAME_MODE_UNSUPPORTED, // 0
+ GAME_MODE_STANDARD, // 1
+ GAME_MODE_PERFORMANCE, // 2
+ GAME_MODE_BATTERY, // 3
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GameMode {}
+
+ public static final int GAME_MODE_UNSUPPORTED = 0;
+ public static final int GAME_MODE_STANDARD = 1;
+ public static final int GAME_MODE_PERFORMANCE = 2;
+ public static final int GAME_MODE_BATTERY = 3;
+
+ public GameManager(Context context, Handler handler) throws ServiceNotFoundException {
+ mContext = context;
+ mService = IGameManagerService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.GAME_SERVICE));
+ }
+
+ @VisibleForTesting
+ public GameManager(Context context, IGameManagerService gameManagerService) {
+ mContext = context;
+ mService = gameManagerService;
+ }
+
+ /**
+ * Returns the game mode for the given package.
+ */
+ // TODO(b/178111358): Add @RequiresPermission.
+ @UserHandleAware
+ public @GameMode int getGameMode(String packageName) {
+ try {
+ return mService.getGameMode(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the game mode for the given package.
+ */
+ // TODO(b/178111358): Add @RequiresPermission.
+ @UserHandleAware
+ public void setGameMode(String packageName, @GameMode int gameMode) {
+ try {
+ mService.setGameMode(packageName, gameMode, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index c2c62c1..9d3286f 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -96,7 +96,14 @@
oneway void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
oneway void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
oneway void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
- oneway void overridePendingTransition(in IBinder token, in String packageName,
+ /**
+ * Overrides the animation of activity pending transition. This call is not one-way because
+ * the method is usually used after startActivity or Activity#finish. If this is non-blocking,
+ * the calling activity may proceed to complete pause and become stopping state, which will
+ * cause the request to be ignored. Besides, startActivity and Activity#finish are blocking
+ * calls, so this method should be the same as them to keep the invocation order.
+ */
+ void overridePendingTransition(in IBinder token, in String packageName,
int enterAnim, int exitAnim);
int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
new file mode 100644
index 0000000..c8e1478
--- /dev/null
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -0,0 +1,25 @@
+/*
+** 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 android.app;
+
+/**
+ * @hide
+ */
+interface IGameManagerService {
+ int getGameMode(String packageName, int userId);
+ void setGameMode(String packageName, int gameMode, int userId);
+}
\ No newline at end of file
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index afa1560..e6aa7a7 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -47,8 +47,19 @@
per-file *Zen* = file:/packages/SystemUI/OWNERS
per-file *StatusBar* = file:/packages/SystemUI/OWNERS
+# PackageManager
+per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file InstantAppResolverService.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file LoadedApk.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file PackageDeleteObserver.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file PackageInstallObserver.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file EphemeralResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+per-file IEphemeralResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+
# ResourcesManager
-per-file ResourcesManager = rtmitchell@google.com, toddke@google.com
+per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com
# VoiceInteraction
per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 8635222..671315f 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
import android.annotation.TestApi;
@@ -1275,8 +1276,10 @@
* @param flags MATCH_* flags from {@link android.content.pm.PackageManager}.
* @hide
*/
+ @SuppressLint("NullableCollection")
@RequiresPermission(permission.GET_INTENT_SENDER_INTENT)
@SystemApi(client = Client.MODULE_LIBRARIES)
+ @TestApi
public @Nullable List<ResolveInfo> queryIntentComponents(@ResolveInfoFlags int flags) {
try {
return ActivityManager.getService()
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index d5f0149..f8c33b5 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -34,6 +34,7 @@
import android.app.role.RoleFrameworkInitializer;
import android.app.search.SearchUiManager;
import android.app.slice.SliceManager;
+import android.app.smartspace.SmartspaceManager;
import android.app.time.TimeManager;
import android.app.timedetector.TimeDetector;
import android.app.timedetector.TimeDetectorImpl;
@@ -119,21 +120,16 @@
import android.media.tv.TvInputManager;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
-import android.net.ConnectivityDiagnosticsManager;
-import android.net.ConnectivityManager;
+import android.net.ConnectivityFrameworkInitializer;
import android.net.EthernetManager;
-import android.net.IConnectivityManager;
import android.net.IEthernetManager;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
-import android.net.ITestNetworkManager;
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
import android.net.NetworkWatchlistManager;
-import android.net.TestNetworkManager;
import android.net.TetheringManager;
-import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
@@ -162,7 +158,6 @@
import android.os.IncidentManager;
import android.os.PowerManager;
import android.os.RecoverySystem;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StatsFrameworkInitializer;
@@ -369,15 +364,6 @@
// (which extends it).
SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
- registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
- new StaticApplicationContextServiceFetcher<ConnectivityManager>() {
- @Override
- public ConnectivityManager createService(Context context) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- return new ConnectivityManager(context, service);
- }});
-
registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
@Override
public IBinder createService() throws ServiceNotFoundException {
@@ -411,50 +397,6 @@
return new IpSecManager(ctx, service);
}});
- registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class,
- new CachedServiceFetcher<VpnManager>() {
- @Override
- public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- return new VpnManager(ctx, service);
- }});
-
- registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
- ConnectivityDiagnosticsManager.class,
- new CachedServiceFetcher<ConnectivityDiagnosticsManager>() {
- @Override
- public ConnectivityDiagnosticsManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
- // ConnectivityDiagnosticsManager is backed by ConnectivityService
- IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- return new ConnectivityDiagnosticsManager(ctx, service);
- }});
-
- registerService(
- Context.TEST_NETWORK_SERVICE,
- TestNetworkManager.class,
- new StaticApplicationContextServiceFetcher<TestNetworkManager>() {
- @Override
- public TestNetworkManager createService(Context context)
- throws ServiceNotFoundException {
- IBinder csBinder =
- ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager csMgr =
- IConnectivityManager.Stub.asInterface(csBinder);
-
- final IBinder tnBinder;
- try {
- tnBinder = csMgr.startOrGetTestNetworkService();
- } catch (RemoteException e) {
- throw new ServiceNotFoundException(Context.TEST_NETWORK_SERVICE);
- }
- ITestNetworkManager tnMgr = ITestNetworkManager.Stub.asInterface(tnBinder);
- return new TestNetworkManager(tnMgr);
- }
- });
-
registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
new StaticServiceFetcher<CountryDetector>() {
@Override
@@ -1223,6 +1165,16 @@
}
});
+ registerService(Context.SMARTSPACE_SERVICE, SmartspaceManager.class,
+ new CachedServiceFetcher<SmartspaceManager>() {
+ @Override
+ public SmartspaceManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(Context.SMARTSPACE_SERVICE);
+ return b == null ? null : new SmartspaceManager(ctx);
+ }
+ });
+
registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class,
new CachedServiceFetcher<AppPredictionManager>() {
@Override
@@ -1426,10 +1378,21 @@
return new MediaMetricsManager(service, ctx.getUserId());
}});
+ registerService(Context.GAME_SERVICE, GameManager.class,
+ new CachedServiceFetcher<GameManager>() {
+ @Override
+ public GameManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ return new GameManager(ctx.getOuterContext(),
+ ctx.mMainThread.getHandler());
+ }
+ });
+
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
// modules.
+ ConnectivityFrameworkInitializer.registerServiceWrappers();
JobSchedulerFrameworkInitializer.registerServiceWrappers();
BlobStoreManagerFrameworkInitializer.initialize();
TelephonyFrameworkInitializer.registerServiceWrappers();
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 623c878d..390d921 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -36,7 +36,6 @@
import android.window.WindowContainerToken;
import java.util.ArrayList;
-import java.util.List;
import java.util.Objects;
/**
@@ -180,6 +179,19 @@
public ActivityInfo topActivityInfo;
/**
+ * The top activity in this task.
+ * @hide
+ */
+ @Nullable
+ public IBinder topActivityToken;
+
+ /**
+ * Whether the direct top activity is in size compat mode on foreground.
+ * @hide
+ */
+ public boolean topActivityInSizeCompat;
+
+ /**
* Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity
* supports), this is what the system actually uses for resizability based on other policy and
* developer options.
@@ -325,6 +337,23 @@
}
/**
+ * @return {@code true} if parameters that are important for size compat have changed.
+ * @hide
+ */
+ public boolean equalsForSizeCompat(@Nullable TaskInfo that) {
+ if (that == null) {
+ return false;
+ }
+ return displayId == that.displayId
+ && taskId == that.taskId
+ && topActivityInSizeCompat == that.topActivityInSizeCompat
+ // TopActivityToken and bounds are important if top activity is in size compat
+ && (!topActivityInSizeCompat || topActivityToken.equals(that.topActivityToken))
+ && (!topActivityInSizeCompat || configuration.windowConfiguration.getBounds()
+ .equals(that.configuration.windowConfiguration.getBounds()));
+ }
+
+ /**
* Reads the TaskInfo from a parcel.
*/
void readFromParcel(Parcel source) {
@@ -356,6 +385,8 @@
parentTaskId = source.readInt();
isFocused = source.readBoolean();
isVisible = source.readBoolean();
+ topActivityToken = source.readStrongBinder();
+ topActivityInSizeCompat = source.readBoolean();
}
/**
@@ -391,6 +422,8 @@
dest.writeInt(parentTaskId);
dest.writeBoolean(isFocused);
dest.writeBoolean(isVisible);
+ dest.writeStrongBinder(topActivityToken);
+ dest.writeBoolean(topActivityInSizeCompat);
}
@Override
@@ -415,6 +448,8 @@
+ " parentTaskId=" + parentTaskId
+ " isFocused=" + isFocused
+ " isVisible=" + isVisible
+ + " topActivityToken=" + topActivityToken
+ + " topActivityInSizeCompat=" + topActivityInSizeCompat
+ "}";
}
}
diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index 15ff531..8b0c706 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -51,6 +51,12 @@
public abstract int getPasswordQuality(@UserIdInt int userHandle);
/**
+ * Caches {@link DevicePolicyManager#getPermissionPolicy(android.content.ComponentName)} of
+ * the given user.
+ */
+ public abstract int getPermissionPolicy(@UserIdInt int userHandle);
+
+ /**
* Empty implementation.
*/
private static class EmptyDevicePolicyCache extends DevicePolicyCache {
@@ -66,5 +72,10 @@
public int getPasswordQuality(int userHandle) {
return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
+
+ @Override
+ public int getPermissionPolicy(int userHandle) {
+ return DevicePolicyManager.PERMISSION_POLICY_PROMPT;
+ }
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ada703b..642bce4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -453,59 +453,6 @@
"android.app.action.PROVISION_FINANCED_DEVICE";
/**
- * Activity action: Starts the provisioning flow which sets up a managed device.
- * Must be started with {@link android.app.Activity#startActivityForResult(Intent, int)}.
- *
- * <p>NOTE: This is only supported on split system user devices, and puts the device into a
- * management state that is distinct from that reached by
- * {@link #ACTION_PROVISION_MANAGED_DEVICE} - specifically the device owner runs on the system
- * user, and only has control over device-wide policies, not individual users and their data.
- * The primary benefit is that multiple non-system users are supported when provisioning using
- * this form of device management.
- *
- * <p>During device owner provisioning a device admin app is set as the owner of the device.
- * A device owner has full control over the device. The device owner can not be modified by the
- * user.
- *
- * <p>A typical use case would be a device that is owned by a company, but used by either an
- * employee or client.
- *
- * <p>An intent with this action can be sent only on an unprovisioned device.
- * It is possible to check if provisioning is allowed or not by querying the method
- * {@link #isProvisioningAllowed(String)}.
- *
- * <p>The intent contains the following extras:
- * <ul>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li>
- * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
- * </ul>
- *
- * <p>When device owner provisioning has completed, an intent of the type
- * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcast to the
- * device owner.
- *
- * <p>From version {@link android.os.Build.VERSION_CODES#O}, when device owner provisioning has
- * completed, along with the above broadcast, activity intent
- * {@link #ACTION_PROVISIONING_SUCCESSFUL} will also be sent to the device owner.
- *
- * <p>If provisioning fails, the device is factory reset.
- *
- * <p>A result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part
- * of the provisioning flow was successful, although this doesn't guarantee the full flow will
- * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
- * that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
- *
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE
- = "android.app.action.PROVISION_MANAGED_SHAREABLE_DEVICE";
-
- /**
* Activity action: Finalizes management provisioning, should be used after user-setup
* has been completed and {@link #getUserProvisioningState()} returns one of:
* <ul>
@@ -1990,8 +1937,8 @@
* Result code for {@link #checkProvisioningPreCondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_PROFILE}, {@link #ACTION_PROVISION_MANAGED_USER} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when provisioning is allowed.
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE} and {@link #ACTION_PROVISION_MANAGED_USER}
+ * when provisioning is allowed.
*
* @hide
*/
@@ -2001,9 +1948,8 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the device already has a device
- * owner.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the device already has a
+ * device owner.
*
* @hide
*/
@@ -2013,9 +1959,8 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the user has a profile owner and for
- * {@link #ACTION_PROVISION_MANAGED_PROFILE} when the profile owner is already set.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user has a profile owner
+ * and for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the profile owner is already set.
*
* @hide
*/
@@ -2025,8 +1970,7 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the user isn't running.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user isn't running.
*
* @hide
*/
@@ -2036,9 +1980,8 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} if the device has already been setup and
- * for {@link #ACTION_PROVISION_MANAGED_USER} if the user has already been setup.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the device has already been
+ * setup and for {@link #ACTION_PROVISION_MANAGED_USER} if the user has already been setup.
*
* @hide
*/
@@ -2064,8 +2007,7 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} if the user is not a system user.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the user is not a system user.
*
* @hide
*/
@@ -2075,9 +2017,8 @@
/**
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} and {@link #ACTION_PROVISION_MANAGED_USER}
- * when the device is a watch and is already paired.
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
+ * {@link #ACTION_PROVISION_MANAGED_USER} when the device is a watch and is already paired.
*
* @hide
*/
@@ -2121,14 +2062,11 @@
/**
* TODO (b/137101239): clean up split system user codes
- * Result code for {@link #checkProvisioningPreCondition}.
- *
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_USER} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} on devices not running with split system
- * user.
*
* @hide
- */
+ * @deprecated not used anymore but can't be removed since it's a @TestApi.
+ **/
+ @Deprecated
@TestApi
public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12;
@@ -2136,8 +2074,7 @@
* Result code for {@link #checkProvisioningPreCondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_PROFILE}, {@link #ACTION_PROVISION_MANAGED_USER} and
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} on devices which do not support device
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE} on devices which do not support device
* admins.
*
* @hide
@@ -2149,11 +2086,10 @@
* TODO (b/137101239): clean up split system user codes
* Result code for {@link #checkProvisioningPreCondition}.
*
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the device the user is a
- * system user on a split system user device.
- *
* @hide
+ * @deprecated not used anymore but can't be removed since it's a @TestApi.
*/
+ @Deprecated
@TestApi
public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
@@ -2969,6 +2905,35 @@
return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation);
}
+ private static final String PREFIX_UNSAFE_OPERATION_REASON = "UNSAFE_OPERATION_REASON_";
+
+ /** @hide */
+ @IntDef(prefix = PREFIX_UNSAFE_OPERATION_REASON, value = {
+ UNSAFE_OPERATION_REASON_NONE,
+ UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public static @interface UnsafeOperationReason {
+ }
+
+ /** @hide */
+ @TestApi
+ public static final int UNSAFE_OPERATION_REASON_NONE = -1;
+
+ /**
+ * Indicates that a {@link UnsafeStateException} was thrown because the operation would distract
+ * the driver of the vehicle.
+ */
+ public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1;
+
+ /** @hide */
+ @NonNull
+ @TestApi
+ public static String unsafeOperationReasonToString(@UnsafeOperationReason int reason) {
+ return DebugUtils.constantToString(DevicePolicyManager.class,
+ PREFIX_UNSAFE_OPERATION_REASON, reason);
+ }
+
/** @hide */
public void resetNewUserDisclaimer() {
if (mService != null) {
@@ -7147,17 +7112,9 @@
}
/**
+ * TODO (b/137101239): remove this method in follow-up CL
+ * since it's only used for split system user.
* Called by a device owner to set whether all users created on the device should be ephemeral.
- * <p>
- * The system user is exempt from this policy - it is never ephemeral.
- * <p>
- * The calling device admin must be the device owner. If it is not, a security exception will be
- * thrown.
- *
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param forceEphemeralUsers If true, all the existing users will be deleted and all
- * subsequently created users will be ephemeral.
- * @throws SecurityException if {@code admin} is not a device owner.
* @hide
*/
public void setForceEphemeralUsers(
@@ -7173,6 +7130,8 @@
}
/**
+ * TODO (b/137101239): remove this method in follow-up CL
+ * since it's only used for split system user.
* @return true if all users are created ephemeral.
* @throws SecurityException if {@code admin} is not a device owner.
* @hide
@@ -10731,9 +10690,7 @@
* profile or user, setting the given package as owner.
*
* @param action One of {@link #ACTION_PROVISION_MANAGED_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_PROFILE},
- * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE},
- * {@link #ACTION_PROVISION_MANAGED_USER}
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE}
* @param packageName The package of the component that would be set as device, user, or profile
* owner.
* @return A {@link ProvisioningPreCondition} value indicating whether provisioning is allowed.
@@ -13170,10 +13127,11 @@
*/
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS)
- public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) {
+ public void setNextOperationSafety(@DevicePolicyOperation int operation,
+ @UnsafeOperationReason int reason) {
if (mService != null) {
try {
- mService.setNextOperationSafety(operation, safe);
+ mService.setNextOperationSafety(operation, reason);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -13270,7 +13228,8 @@
return null;
}
try {
- return mService.createAndProvisionManagedProfile(provisioningParams);
+ return mService.createAndProvisionManagedProfile(
+ provisioningParams, mContext.getPackageName());
} catch (ServiceSpecificException e) {
throw new ProvisioningException(e, e.errorCode);
} catch (RemoteException e) {
@@ -13301,7 +13260,7 @@
throws ProvisioningException {
if (mService != null) {
try {
- mService.provisionFullyManagedDevice(provisioningParams);
+ mService.provisionFullyManagedDevice(provisioningParams, mContext.getPackageName());
} catch (ServiceSpecificException e) {
throw new ProvisioningException(e, e.errorCode);
} catch (RemoteException re) {
diff --git a/core/java/android/app/admin/DevicePolicySafetyChecker.java b/core/java/android/app/admin/DevicePolicySafetyChecker.java
index b1a80c5..6c6f2aa 100644
--- a/core/java/android/app/admin/DevicePolicySafetyChecker.java
+++ b/core/java/android/app/admin/DevicePolicySafetyChecker.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import com.android.internal.os.IResultReceiver;
@@ -30,14 +31,16 @@
/**
* Returns whether the given {@code operation} can be safely executed at the moment.
*/
- boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation);
+ @UnsafeOperationReason
+ int getUnsafeOperationReason(@DevicePolicyOperation int operation);
/**
* Returns a new exception for when the given {@code operation} cannot be safely executed.
*/
@NonNull
- default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation) {
- return new UnsafeStateException(operation);
+ default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation,
+ @UnsafeOperationReason int reason) {
+ return new UnsafeStateException(operation, reason);
}
/**
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3765a67..f9ee153 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -491,11 +491,11 @@
void setManagedProfileMaximumTimeOff(in ComponentName admin, long timeoutMs);
boolean canProfileOwnerResetPasswordWhenLocked(int userId);
- void setNextOperationSafety(int operation, boolean safe);
+ void setNextOperationSafety(int operation, int reason);
String getEnrollmentSpecificId(String callerPackage);
void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId);
- UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams);
- void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams);
+ UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
+ void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
}
diff --git a/core/java/android/app/admin/UnsafeStateException.java b/core/java/android/app/admin/UnsafeStateException.java
index 9dcaae4..56eeb06 100644
--- a/core/java/android/app/admin/UnsafeStateException.java
+++ b/core/java/android/app/admin/UnsafeStateException.java
@@ -15,15 +15,21 @@
*/
package android.app.admin;
+import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION;
+import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
+
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
/**
- * Exception thrown when a {@link DevicePolicyManager} operation failed because it was not safe
- * to be executed at that moment.
+ * Exception thrown when a {@link android.app.admin.DevicePolicyManager} operation failed because it
+ * was not safe to be executed at that moment.
*
* <p>For example, it can be thrown on
* {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive devices} when the vehicle
@@ -33,12 +39,19 @@
public final class UnsafeStateException extends IllegalStateException implements Parcelable {
private final @DevicePolicyOperation int mOperation;
+ private final @UnsafeOperationReason int mReason;
/** @hide */
- public UnsafeStateException(@DevicePolicyOperation int operation) {
+ @TestApi
+ public UnsafeStateException(@DevicePolicyOperation int operation,
+ @UnsafeOperationReason int reason) {
super();
-
+ Preconditions.checkArgument(reason == UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
+ "invalid reason %d, must be %d (%s)", reason,
+ UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
+ unsafeOperationReasonToString(UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION));
mOperation = operation;
+ mReason = reason;
}
/** @hide */
@@ -47,6 +60,22 @@
return mOperation;
}
+ /**
+ * Gets the reason the operation is unsafe.
+ *
+ * @return currently, only valid reason is
+ * {@link android.app.admin.DevicePolicyManager#UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION}.
+ */
+ public @UnsafeOperationReason int getReason() {
+ return mReason;
+ }
+
+ /** @hide */
+ @Override
+ public String getMessage() {
+ return DevicePolicyManager.unsafeOperationReasonToString(mReason);
+ }
+
@Override
public int describeContents() {
return 0;
@@ -55,6 +84,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mOperation);
+ dest.writeInt(mReason);
}
@NonNull
@@ -63,7 +93,7 @@
@Override
public UnsafeStateException createFromParcel(Parcel source) {
- return new UnsafeStateException(source.readInt());
+ return new UnsafeStateException(source.readInt(), source.readInt());
}
@Override
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 65a2164..2b52875 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2,6 +2,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Activity;
import android.content.ComponentName;
@@ -1542,6 +1543,7 @@
* {@link View#getOnReceiveContentMimeTypes()} for details.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public String[] getOnReceiveContentMimeTypes() {
return mOnReceiveContentMimeTypes;
}
diff --git a/core/java/android/app/search/Query.java b/core/java/android/app/search/Query.java
index 447ca31..3ab20bb 100644
--- a/core/java/android/app/search/Query.java
+++ b/core/java/android/app/search/Query.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
@@ -46,6 +47,7 @@
public Query(@NonNull String input,
long timestamp,
+ @SuppressLint("NullableCollection")
@Nullable Bundle extras) {
mInput = input;
mTimestamp = timestamp;
@@ -69,6 +71,7 @@
}
@Nullable
+ @SuppressLint("NullableCollection")
public Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/app/search/SearchAction.java b/core/java/android/app/search/SearchAction.java
index a76154a..9e40e7e 100644
--- a/core/java/android/app/search/SearchAction.java
+++ b/core/java/android/app/search/SearchAction.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Intent;
@@ -167,6 +168,7 @@
/**
* Returns the extra bundle for this object.
*/
+ @SuppressLint("NullableCollection")
public @Nullable Bundle getExtras() {
return mExtras;
}
@@ -325,7 +327,8 @@
* Sets the extra.
*/
@NonNull
- public SearchAction.Builder setExtras(@Nullable Bundle extras) {
+ public SearchAction.Builder setExtras(
+ @SuppressLint("NullableCollection") @Nullable Bundle extras) {
mExtras = extras;
return this;
}
diff --git a/core/java/android/app/search/SearchContext.java b/core/java/android/app/search/SearchContext.java
index 9bf766d..548b7da 100644
--- a/core/java/android/app/search/SearchContext.java
+++ b/core/java/android/app/search/SearchContext.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
@@ -52,7 +53,7 @@
public SearchContext(int resultTypes,
int queryTimeoutMillis,
- @Nullable Bundle extras) {
+ @SuppressLint("NullableCollection") @Nullable Bundle extras) {
mResultTypes = resultTypes;
mTimeoutMillis = queryTimeoutMillis;
mExtras = extras;
@@ -83,6 +84,7 @@
}
@Nullable
+ @SuppressLint("NullableCollection")
public Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java
index cac22d81..6a80f8b 100644
--- a/core/java/android/app/search/SearchTarget.java
+++ b/core/java/android/app/search/SearchTarget.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.appwidget.AppWidgetProviderInfo;
import android.content.pm.ShortcutInfo;
@@ -224,6 +225,7 @@
* Return extra bundle.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public Bundle getExtras() {
return mExtras;
}
@@ -386,7 +388,7 @@
* TODO: add comment
*/
@NonNull
- public Builder setExtras(@Nullable Bundle extras) {
+ public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
mExtras = extras;
return this;
}
diff --git a/core/java/android/net/VpnInfo.aidl b/core/java/android/app/smartspace/ISmartspaceCallback.aidl
similarity index 69%
copy from core/java/android/net/VpnInfo.aidl
copy to core/java/android/app/smartspace/ISmartspaceCallback.aidl
index 8bcaa81..df105f9 100644
--- a/core/java/android/net/VpnInfo.aidl
+++ b/core/java/android/app/smartspace/ISmartspaceCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,14 @@
* limitations under the License.
*/
-package android.net;
+package android.app.smartspace;
-parcelable VpnInfo;
+import android.content.pm.ParceledListSlice;
+
+/**
+ * @hide
+ */
+oneway interface ISmartspaceCallback {
+
+ void onResult(in ParceledListSlice result);
+}
diff --git a/core/java/android/app/smartspace/ISmartspaceManager.aidl b/core/java/android/app/smartspace/ISmartspaceManager.aidl
new file mode 100644
index 0000000..e7ec889
--- /dev/null
+++ b/core/java/android/app/smartspace/ISmartspaceManager.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.smartspace;
+
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.ISmartspaceCallback;
+import android.content.pm.ParceledListSlice;
+
+/**
+ * @hide
+ */
+interface ISmartspaceManager {
+
+ void createSmartspaceSession(in SmartspaceConfig config, in SmartspaceSessionId sessionId,
+ in IBinder token);
+
+ void notifySmartspaceEvent(in SmartspaceSessionId sessionId, in SmartspaceTargetEvent event);
+
+ void requestSmartspaceUpdate(in SmartspaceSessionId sessionId);
+
+ void registerSmartspaceUpdates(in SmartspaceSessionId sessionId,
+ in ISmartspaceCallback callback);
+
+ void unregisterSmartspaceUpdates(in SmartspaceSessionId sessionId,
+ in ISmartspaceCallback callback);
+
+ void destroySmartspaceSession(in SmartspaceSessionId sessionId);
+}
diff --git a/core/java/android/app/smartspace/OWNERS b/core/java/android/app/smartspace/OWNERS
new file mode 100644
index 0000000..19ef9d7
--- /dev/null
+++ b/core/java/android/app/smartspace/OWNERS
@@ -0,0 +1,2 @@
+srazdan@google.com
+alexmang@google.com
\ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceAction.java b/core/java/android/app/smartspace/SmartspaceAction.java
new file mode 100644
index 0000000..439d851
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceAction.java
@@ -0,0 +1,355 @@
+/*
+ * 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.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceAction} represents an action which can be taken by a user by tapping on either
+ * the title, the subtitle or on the icon. Supported instances are Intents, PendingIntents or a
+ * ShortcutInfo (by putting the ShortcutInfoId in the bundle). These actions can be called from
+ * another process or within the client process.
+ *
+ * Clients can also receive conditional Intents/PendingIntents in the extras bundle which are
+ * supposed to be fired when the conditions are met. For example, a user can invoke a dismiss/block
+ * action on a game score card but the intention is to only block the team and not the entire
+ * feature.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceAction implements Parcelable {
+
+ private static final String TAG = "SmartspaceAction";
+
+ /** A unique Id of this {@link SmartspaceAction}. */
+ @NonNull
+ private final String mId;
+
+ /** An Icon which can be displayed in the UI. */
+ @Nullable
+ private final Icon mIcon;
+
+ /** Title associated with an action. */
+ @NonNull
+ private final CharSequence mTitle;
+
+ /** Subtitle associated with an action. */
+ @Nullable
+ private final CharSequence mSubtitle;
+
+ @Nullable
+ private final CharSequence mContentDescription;
+
+ @Nullable
+ private final PendingIntent mPendingIntent;
+
+ @Nullable
+ private final Intent mIntent;
+
+ @Nullable
+ private final UserHandle mUserHandle;
+
+ @Nullable
+ private Bundle mExtras;
+
+ SmartspaceAction(Parcel in) {
+ mId = in.readString();
+ mIcon = in.readTypedObject(Icon.CREATOR);
+ mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+ mIntent = in.readTypedObject(Intent.CREATOR);
+ mUserHandle = in.readTypedObject(UserHandle.CREATOR);
+ mExtras = in.readTypedObject(Bundle.CREATOR);
+ }
+
+ private SmartspaceAction(
+ @NonNull String id,
+ @Nullable Icon icon,
+ @NonNull CharSequence title,
+ @Nullable CharSequence subtitle,
+ @Nullable CharSequence contentDescription,
+ @Nullable PendingIntent pendingIntent,
+ @Nullable Intent intent,
+ @Nullable UserHandle userHandle,
+ @Nullable Bundle extras) {
+ mId = Objects.requireNonNull(id);
+ mIcon = icon;
+ mTitle = Objects.requireNonNull(title);
+ mSubtitle = subtitle;
+ mContentDescription = contentDescription;
+ mPendingIntent = pendingIntent;
+ mIntent = intent;
+ mUserHandle = userHandle;
+ mExtras = extras;
+ }
+
+ /**
+ * Returns the unique id of this object.
+ */
+ public @NonNull String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns an icon representing the action.
+ */
+ public @Nullable Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Returns a title representing the action.
+ */
+ public @NonNull CharSequence getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Returns a subtitle representing the action.
+ */
+ public @Nullable CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
+ /**
+ * Returns a content description representing the action.
+ */
+ public @Nullable CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * Returns the action intent.
+ */
+ public @Nullable PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
+ /**
+ * Returns the intent.
+ */
+ public @Nullable Intent getIntent() {
+ return mIntent;
+ }
+
+ /**
+ * Returns the user handle.
+ */
+ public @Nullable UserHandle getUserHandle() {
+ return mUserHandle;
+ }
+
+ /**
+ * Returns the extra bundle for this object.
+ */
+ @SuppressLint("NullableCollection")
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceAction)) return false;
+ SmartspaceAction that = (SmartspaceAction) o;
+ return mId.equals(that.mId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(mId);
+ out.writeTypedObject(mIcon, flags);
+ TextUtils.writeToParcel(mTitle, out, flags);
+ TextUtils.writeToParcel(mSubtitle, out, flags);
+ TextUtils.writeToParcel(mContentDescription, out, flags);
+ out.writeTypedObject(mPendingIntent, flags);
+ out.writeTypedObject(mIntent, flags);
+ out.writeTypedObject(mUserHandle, flags);
+ out.writeBundle(mExtras);
+ }
+
+ @Override
+ public String toString() {
+ return "SmartspaceAction{"
+ + "mId='" + mId + '\''
+ + ", mIcon=" + mIcon
+ + ", mTitle=" + mTitle
+ + ", mSubtitle=" + mSubtitle
+ + ", mContentDescription=" + mContentDescription
+ + ", mPendingIntent=" + mPendingIntent
+ + ", mIntent=" + mIntent
+ + ", mUserHandle=" + mUserHandle
+ + ", mExtras=" + mExtras
+ + '}';
+ }
+
+ public static final @NonNull Creator<SmartspaceAction> CREATOR =
+ new Creator<SmartspaceAction>() {
+ public SmartspaceAction createFromParcel(Parcel in) {
+ return new SmartspaceAction(in);
+ }
+ public SmartspaceAction[] newArray(int size) {
+ return new SmartspaceAction[size];
+ }
+ };
+
+ /**
+ * A builder for Smartspace action object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ @NonNull
+ private String mId;
+
+ @Nullable
+ private Icon mIcon;
+
+ @NonNull
+ private CharSequence mTitle;
+
+ @Nullable
+ private CharSequence mSubtitle;
+
+ @Nullable
+ private CharSequence mContentDescription;
+
+ @Nullable
+ private PendingIntent mPendingIntent;
+
+ @Nullable
+ private Intent mIntent;
+
+ @Nullable
+ private UserHandle mUserHandle;
+
+ @Nullable
+ private Bundle mExtras;
+
+ /**
+ * Id and title are required.
+ */
+ public Builder(@NonNull String id, @NonNull String title) {
+ mId = Objects.requireNonNull(id);
+ mTitle = Objects.requireNonNull(title);
+ }
+
+ /**
+ * Sets the icon.
+ */
+ @NonNull
+ public Builder setIcon(
+ @Nullable Icon icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Sets the subtitle.
+ */
+ @NonNull
+ public Builder setSubtitle(
+ @Nullable CharSequence subtitle) {
+ mSubtitle = subtitle;
+ return this;
+ }
+
+ /**
+ * Sets the content description.
+ */
+ @NonNull
+ public Builder setContentDescription(
+ @Nullable CharSequence contentDescription) {
+ mContentDescription = contentDescription;
+ return this;
+ }
+
+ /**
+ * Sets the pending intent.
+ */
+ @NonNull
+ public Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+ mPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
+ * Sets the user handle.
+ */
+ @NonNull
+ public Builder setUserHandle(@Nullable UserHandle userHandle) {
+ mUserHandle = userHandle;
+ return this;
+ }
+
+ /**
+ * Sets the intent.
+ */
+ @NonNull
+ public Builder setIntent(@Nullable Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ /**
+ * Sets the extra.
+ */
+ @NonNull
+ public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceAction instance.
+ *
+ * @throws IllegalStateException if no target is set
+ */
+ @NonNull
+ public SmartspaceAction build() {
+ return new SmartspaceAction(mId, mIcon, mTitle, mSubtitle, mContentDescription,
+ mPendingIntent, mIntent, mUserHandle, mExtras);
+ }
+ }
+}
diff --git a/core/java/android/net/VpnInfo.aidl b/core/java/android/app/smartspace/SmartspaceConfig.aidl
similarity index 74%
copy from core/java/android/net/VpnInfo.aidl
copy to core/java/android/app/smartspace/SmartspaceConfig.aidl
index 8bcaa81..136b6f4 100644
--- a/core/java/android/net/VpnInfo.aidl
+++ b/core/java/android/app/smartspace/SmartspaceConfig.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
+/**
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net;
+package android.app.smartspace;
-parcelable VpnInfo;
+parcelable SmartspaceConfig;
\ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceConfig.java b/core/java/android/app/smartspace/SmartspaceConfig.java
new file mode 100644
index 0000000..07d7bf0
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceConfig.java
@@ -0,0 +1,204 @@
+/*
+ * 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.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceConfig} instance is supposed to be created by a smartspace client for each
+ * UISurface. The client can specify some initialization conditions for the UISurface like its name,
+ * expected number of smartspace cards etc. The clients can also specify if they want periodic
+ * updates or their desired maximum refresh frequency.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceConfig implements Parcelable {
+
+ /**
+ * The least number of smartspace targets expected to be predicted by the backend. The backend
+ * will always try to satisfy this threshold but it is not guaranteed to always meet it.
+ */
+ private final int mSmartspaceTargetCount;
+
+ /**
+ * A {@link mUiSurface} is the name of the surface which will be used to display the cards. A
+ * few examples are homescreen, lockscreen, aod etc.
+ */
+ @NonNull
+ private final String mUiSurface;
+
+ /** Package name of the client. */
+ @NonNull
+ private String mPackageName;
+
+ /** Send other client UI configurations in extras.
+ *
+ * This can include:
+ *
+ * - Desired maximum update frequency
+ * - Request to get periodic updates
+ * - Request to support multiple clients for the same UISurface.
+ */
+ @Nullable
+ private final Bundle mExtras;
+
+ private SmartspaceConfig(@NonNull String uiSurface, int numPredictedTargets,
+ @NonNull String packageName, @Nullable Bundle extras) {
+ mUiSurface = uiSurface;
+ mSmartspaceTargetCount = numPredictedTargets;
+ mPackageName = packageName;
+ mExtras = extras;
+ }
+
+ private SmartspaceConfig(Parcel parcel) {
+ mUiSurface = parcel.readString();
+ mSmartspaceTargetCount = parcel.readInt();
+ mPackageName = parcel.readString();
+ mExtras = parcel.readBundle();
+ }
+
+ /** Returns the package name of the prediction context. */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /** Returns the number of smartspace targets requested by the user. */
+ @NonNull
+ public int getSmartspaceTargetCount() {
+ return mSmartspaceTargetCount;
+ }
+
+ /** Returns the UISurface requested by the client. */
+ @NonNull
+ public String getUiSurface() {
+ return mUiSurface;
+ }
+
+ @Nullable
+ @SuppressLint("NullableCollection")
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mUiSurface);
+ dest.writeInt(mSmartspaceTargetCount);
+ dest.writeString(mPackageName);
+ dest.writeBundle(mExtras);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SmartspaceConfig that = (SmartspaceConfig) o;
+ return mSmartspaceTargetCount == that.mSmartspaceTargetCount
+ && Objects.equals(mUiSurface, that.mUiSurface)
+ && Objects.equals(mPackageName, that.mPackageName)
+ && Objects.equals(mExtras, that.mExtras);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSmartspaceTargetCount, mUiSurface, mPackageName, mExtras);
+ }
+
+ /**
+ * @see Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceConfig> CREATOR =
+ new Creator<SmartspaceConfig>() {
+ public SmartspaceConfig createFromParcel(Parcel parcel) {
+ return new SmartspaceConfig(parcel);
+ }
+
+ public SmartspaceConfig[] newArray(int size) {
+ return new SmartspaceConfig[size];
+ }
+ };
+
+ /**
+ * A builder for {@link SmartspaceConfig}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ @NonNull
+ private int mSmartspaceTargetCount = 5; // Default count is 5
+ @NonNull
+ private final String mUiSurface;
+ @NonNull
+ private final String mPackageName;
+ @NonNull
+ private Bundle mExtras = Bundle.EMPTY;
+
+ /**
+ * @param context The {@link Context} which is used to fetch the package name.
+ * @param uiSurface the UI Surface name associated with this context.
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull Context context, @NonNull String uiSurface) {
+ mPackageName = context.getPackageName();
+ this.mUiSurface = uiSurface;
+ }
+
+ /**
+ * Used to set the expected number of cards for this context.
+ */
+ @NonNull
+ public Builder setSmartspaceTargetCount(int smartspaceTargetCount) {
+ this.mSmartspaceTargetCount = smartspaceTargetCount;
+ return this;
+ }
+
+ /**
+ * Used to send a bundle containing extras for the {@link SmartspaceConfig}.
+ */
+ @NonNull
+ public Builder setExtras(@SuppressLint("NullableCollection") @NonNull Bundle extras) {
+ this.mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Returns an instance of {@link SmartspaceConfig}.
+ */
+ @NonNull
+ public SmartspaceConfig build() {
+ return new SmartspaceConfig(mUiSurface, mSmartspaceTargetCount, mPackageName, mExtras);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceManager.java b/core/java/android/app/smartspace/SmartspaceManager.java
new file mode 100644
index 0000000..ff5eb10
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceManager.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 android.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+
+import java.util.Objects;
+
+/**
+ * Smartspace is a container in Android which is used to show contextual content powered by the
+ * intelligence service running on the device. A smartspace container can be on AoD, lockscreen or
+ * on the homescreen and can show personalized cards which are either derived from on device or
+ * online signals.
+ *
+ * {@link SmartspaceManager} is a system service that provides methods to create Smartspace session
+ * clients. An instance of this class is returned when a client calls
+ * <code> context.getSystemService("smartspace"); </code>.
+ *
+ * After receiving the service, a client must call
+ * {@link SmartspaceManager#createSmartspaceSession(SmartspaceConfig)} with a corresponding
+ * {@link SmartspaceConfig} to get an instance of {@link SmartspaceSession}.
+ * This session is then a client's point of contact with the api. They can send events, request for
+ * updates using the session. It is client's duty to call {@link SmartspaceSession#destroy()} to
+ * destroy the session once they no longer need it.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceManager {
+
+ private final Context mContext;
+
+ /**
+ * @hide
+ */
+ public SmartspaceManager(Context context) {
+ mContext = Objects.requireNonNull(context);
+ }
+
+ /**
+ * Creates a new Smartspace session.
+ */
+ @NonNull
+ public SmartspaceSession createSmartspaceSession(
+ @NonNull SmartspaceConfig smartspaceConfig) {
+ return new SmartspaceSession(mContext, smartspaceConfig);
+ }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceSession.java b/core/java/android/app/smartspace/SmartspaceSession.java
new file mode 100644
index 0000000..16def61
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceSession.java
@@ -0,0 +1,284 @@
+/*
+ * 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.smartspace;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.smartspace.ISmartspaceCallback.Stub;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import dalvik.system.CloseGuard;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+/**
+ * Client API to share information about the Smartspace UI state and execute query.
+ *
+ * <p>
+ * Usage: <pre> {@code
+ *
+ * class MyActivity {
+ * private SmartspaceSession mSmartspaceSession;
+ *
+ * void onCreate() {
+ * mSmartspaceSession = mSmartspaceManager.createSmartspaceSession(smartspaceConfig)
+ * mSmartspaceSession.registerSmartspaceUpdates(...)
+ * }
+ *
+ * void onStart() {
+ * mSmartspaceSession.requestSmartspaceUpdate()
+ * }
+ *
+ * void onTouch(...) OR
+ * void onStateTransitionStarted(...) OR
+ * void onResume(...) OR
+ * void onStop(...) {
+ * mSmartspaceSession.notifyEvent(event);
+ * }
+ *
+ * void onDestroy() {
+ * mSmartspaceSession.unregisterPredictionUpdates()
+ * mSmartspaceSession.destroy();
+ * }
+ *
+ * }</pre>
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSession implements AutoCloseable {
+
+ private static final String TAG = SmartspaceSession.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private final android.app.smartspace.ISmartspaceManager mInterface;
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+ private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
+
+ private final SmartspaceSessionId mSessionId;
+ private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>();
+ private final IBinder mToken = new Binder();
+
+ /**
+ * Creates a new Smartspace ui client.
+ * <p>
+ * The caller should call {@link SmartspaceSession#destroy()} to dispose the client once it
+ * no longer used.
+ *
+ * @param context the {@link Context} of the user of this {@link SmartspaceSession}.
+ * @param smartspaceConfig the Smartspace context.
+ */
+ // b/177858121 Create weak reference child objects to not leak context.
+ SmartspaceSession(@NonNull Context context, @NonNull SmartspaceConfig smartspaceConfig) {
+ IBinder b = ServiceManager.getService(Context.SMARTSPACE_SERVICE);
+ mInterface = android.app.smartspace.ISmartspaceManager.Stub.asInterface(b);
+ mSessionId = new SmartspaceSessionId(
+ context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
+ try {
+ mInterface.createSmartspaceSession(smartspaceConfig, mSessionId, mToken);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to cerate Smartspace session", e);
+ e.rethrowFromSystemServer();
+ }
+
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * Notifies the Smartspace service of a Smartspace target event.
+ *
+ * @param event The {@link SmartspaceTargetEvent} that represents the Smartspace target event.
+ */
+ public void notifySmartspaceEvent(@NonNull SmartspaceTargetEvent event) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+ try {
+ mInterface.notifySmartspaceEvent(mSessionId, event);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify event", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the smartspace service for an update.
+ */
+ public void requestSmartspaceUpdate() {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+ try {
+ mInterface.requestSmartspaceUpdate(mSessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to request update.", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the smartspace service provide continuous updates of smartspace cards via the
+ * provided callback, until the given callback is unregistered.
+ *
+ * @param callbackExecutor The callback executor to use when calling the callback.
+ * @param callback The Callback to be called when updates of Smartspace targets are
+ * available.
+ */
+ public void registerSmartspaceUpdates(@NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull Callback callback) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+
+ if (mRegisteredCallbacks.containsKey(callback)) {
+ // Skip if this callback is already registered
+ return;
+ }
+ try {
+ final CallbackWrapper callbackWrapper = new CallbackWrapper(callbackExecutor,
+ callback::onTargetsAvailable);
+ mRegisteredCallbacks.put(callback, callbackWrapper);
+ mInterface.registerSmartspaceUpdates(mSessionId, callbackWrapper);
+ mInterface.requestSmartspaceUpdate(mSessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register for smartspace updates", e);
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Requests the smartspace service to stop providing continuous updates to the provided
+ * callback until the callback is re-registered.
+ *
+ * @see {@link SmartspaceSession#registerSmartspaceUpdates(Executor, Callback)}.
+ *
+ * @param callback The callback to be unregistered.
+ */
+ public void unregisterSmartspaceUpdates(@NonNull Callback callback) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+
+ if (!mRegisteredCallbacks.containsKey(callback)) {
+ // Skip if this callback was never registered
+ return;
+ }
+ try {
+ final CallbackWrapper callbackWrapper = mRegisteredCallbacks.remove(callback);
+ mInterface.unregisterSmartspaceUpdates(mSessionId, callbackWrapper);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister for smartspace updates", e);
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Destroys the client and unregisters the callback. Any method on this class after this call
+ * will throw {@link IllegalStateException}.
+ */
+ public void destroy() {
+ if (!mIsClosed.getAndSet(true)) {
+ mCloseGuard.close();
+
+ // Do destroy;
+ try {
+ mInterface.destroySmartspaceSession(mSessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify Smartspace target event", e);
+ e.rethrowFromSystemServer();
+ }
+ } else {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ try {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ if (!mIsClosed.get()) {
+ destroy();
+ }
+ } finally {
+ try {
+ super.finalize();
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ finalize();
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ }
+
+ /**
+ * Callback for receiving smartspace updates.
+ */
+ public interface Callback {
+
+ /**
+ * Called when a new set of smartspace targets are available.
+ *
+ * @param targets Sorted list of smartspace targets.
+ */
+ void onTargetsAvailable(@NonNull List<SmartspaceTarget> targets);
+ }
+
+ static class CallbackWrapper extends Stub {
+
+ private final Consumer<List<SmartspaceTarget>> mCallback;
+ private final Executor mExecutor;
+
+ CallbackWrapper(@NonNull Executor callbackExecutor,
+ @NonNull Consumer<List<SmartspaceTarget>> callback) {
+ mCallback = callback;
+ mExecutor = callbackExecutor;
+ }
+
+ @Override
+ public void onResult(ParceledListSlice result) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "CallbackWrapper.onResult result=" + result.getList());
+ }
+ mExecutor.execute(() -> mCallback.accept(result.getList()));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+}
diff --git a/core/java/android/net/VpnInfo.aidl b/core/java/android/app/smartspace/SmartspaceSessionId.aidl
similarity index 74%
copy from core/java/android/net/VpnInfo.aidl
copy to core/java/android/app/smartspace/SmartspaceSessionId.aidl
index 8bcaa81..a864412 100644
--- a/core/java/android/net/VpnInfo.aidl
+++ b/core/java/android/app/smartspace/SmartspaceSessionId.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
+/**
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net;
+package android.app.smartspace;
-parcelable VpnInfo;
+parcelable SmartspaceSessionId;
\ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceSessionId.java b/core/java/android/app/smartspace/SmartspaceSessionId.java
new file mode 100644
index 0000000..5220c35
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceSessionId.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.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The id for an Smartspace session. See {@link SmartspaceSession}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSessionId implements Parcelable {
+
+ @NonNull
+ private final String mId;
+
+ @NonNull
+ private final int mUserId;
+
+ /**
+ * Creates a new id for a Smartspace session.
+ *
+ * @hide
+ */
+ public SmartspaceSessionId(@NonNull final String id, @NonNull final int userId) {
+ mId = id;
+ mUserId = userId;
+ }
+
+ private SmartspaceSessionId(Parcel p) {
+ mId = p.readString();
+ mUserId = p.readInt();
+ }
+
+ /**
+ * Returns a {@link String} Id of this sessionId.
+ */
+ @Nullable
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the userId associated with this sessionId.
+ */
+ @NonNull
+ public int getUserId() {
+ return mUserId;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (!getClass().equals(o != null ? o.getClass() : null)) return false;
+
+ SmartspaceSessionId other = (SmartspaceSessionId) o;
+ return mId.equals(other.mId) && mUserId == other.mUserId;
+ }
+
+ @Override
+ public String toString() {
+ return "SmartspaceSessionId{"
+ + "mId='" + mId + '\''
+ + ", mUserId=" + mUserId
+ + '}';
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId, mUserId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeInt(mUserId);
+ }
+
+ public static final @NonNull Creator<SmartspaceSessionId> CREATOR =
+ new Creator<SmartspaceSessionId>() {
+ public SmartspaceSessionId createFromParcel(Parcel parcel) {
+ return new SmartspaceSessionId(parcel);
+ }
+
+ public SmartspaceSessionId[] newArray(int size) {
+ return new SmartspaceSessionId[size];
+ }
+ };
+}
diff --git a/core/java/android/net/VpnInfo.aidl b/core/java/android/app/smartspace/SmartspaceTarget.aidl
similarity index 74%
copy from core/java/android/net/VpnInfo.aidl
copy to core/java/android/app/smartspace/SmartspaceTarget.aidl
index 8bcaa81..3442cf2 100644
--- a/core/java/android/net/VpnInfo.aidl
+++ b/core/java/android/app/smartspace/SmartspaceTarget.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
+/**
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net;
+package android.app.smartspace;
-parcelable VpnInfo;
+parcelable SmartspaceTarget;
\ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.java b/core/java/android/app/smartspace/SmartspaceTarget.java
new file mode 100644
index 0000000..ce5040e
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTarget.java
@@ -0,0 +1,660 @@
+/*
+ * 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.smartspace;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceTarget} is a data class which holds all properties necessary to inflate a
+ * smartspace card. It contains data and related metadata which is supposed to be utilized by
+ * smartspace clients based on their own UI/UX requirements. Some of the properties have
+ * {@link SmartspaceAction} as their type because they can have associated actions.
+ *
+ * <p><b>NOTE: </b>
+ * If {@link mWidgetId} is set, it should be preferred over all other properties.
+ * Else, if {@link mSliceUri} is set, it should be preferred over all other data properties.
+ * Otherwise, the instance should be treated as a data object.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceTarget implements Parcelable {
+
+ /** A unique Id for an instance of {@link SmartspaceTarget}. */
+ @NonNull
+ private final String mSmartspaceTargetId;
+
+ /** A {@link SmartspaceAction} for the header in the Smartspace card. */
+ @Nullable
+ private final SmartspaceAction mHeaderAction;
+
+ /** A {@link SmartspaceAction} for the base action in the Smartspace card. */
+ @Nullable
+ private final SmartspaceAction mBaseAction;
+
+ /** A timestamp indicating when the card was created. */
+ @NonNull
+ private final long mCreationTimeMillis;
+
+ /**
+ * A timestamp indicating when the card should be removed from view, in case the service
+ * disconnects or restarts.
+ */
+ @NonNull
+ private final long mExpiryTimeMillis;
+
+ /** A score assigned to a target. */
+ @NonNull
+ private final float mScore;
+
+ /** A {@link List<SmartspaceAction>} containing all action chips. */
+ @NonNull
+ private final List<SmartspaceAction> mActionChips;
+
+ /** A {@link List<SmartspaceAction>} containing all icons for the grid. */
+ @NonNull
+ private final List<SmartspaceAction> mIconGrid;
+
+ /**
+ * {@link FeatureType} indicating the feature type of this card.
+ *
+ * @see FeatureType
+ */
+ @FeatureType
+ @NonNull
+ private final int mFeatureType;
+
+ /**
+ * Indicates whether the content is sensitive. Certain UI surfaces may choose to skip rendering
+ * real content until the device is unlocked.
+ */
+ @NonNull
+ private final boolean mSensitive;
+
+ /** Indicating if the UI should show this target in its expanded state. */
+ @NonNull
+ private final boolean mShouldShowExpanded;
+
+ /** A Notification key if the target was generated using a notification. */
+ @Nullable
+ private final String mSourceNotificationKey;
+
+ /** {@link ComponentName} for this target. */
+ @NonNull
+ private final ComponentName mComponentName;
+
+ /** {@link UserHandle} for this target. */
+ @NonNull
+ private final UserHandle mUserHandle;
+
+ /** Target Ids of other {@link SmartspaceTarget}s if they are associated with this target. */
+ @Nullable
+ private final String mAssociatedSmartspaceTargetId;
+
+ /** {@link Uri} Slice Uri if this target is a slice. */
+ @Nullable
+ private final Uri mSliceUri;
+
+ /** {@link AppWidgetProviderInfo} if this target is a widget. */
+ @Nullable
+ private final AppWidgetProviderInfo mWidgetId;
+
+ public static final int FEATURE_UNDEFINED = 0;
+ public static final int FEATURE_WEATHER = 1;
+ public static final int FEATURE_CALENDAR = 2;
+ public static final int FEATURE_COMMUTE_TIME = 3;
+ public static final int FEATURE_FLIGHT = 4;
+ public static final int FEATURE_TIPS = 5;
+ public static final int FEATURE_REMINDER = 6;
+ public static final int FEATURE_ALARM = 7;
+ public static final int FEATURE_ONBOARDING = 8;
+ public static final int FEATURE_SPORTS = 9;
+ public static final int FEATURE_WEATHER_ALERT = 10;
+ public static final int FEATURE_CONSENT = 11;
+ public static final int FEATURE_STOCK_PRICE_CHANGE = 12;
+ public static final int FEATURE_SHOPPING_LIST = 13;
+ public static final int FEATURE_LOYALTY_CARD = 14;
+ public static final int FEATURE_MEDIA = 15;
+ public static final int FEATURE_BEDTIME_ROUTINE = 16;
+ public static final int FEATURE_FITNESS_TRACKING = 17;
+ public static final int FEATURE_ETA_MONITORING = 18;
+ public static final int FEATURE_MISSED_CALL = 19;
+ public static final int FEATURE_PACKAGE_TRACKING = 20;
+ public static final int FEATURE_TIMER = 21;
+ public static final int FEATURE_STOPWATCH = 22;
+ public static final int FEATURE_UPCOMING_ALARM = 23;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"FEATURE_"}, value = {
+ FEATURE_UNDEFINED,
+ FEATURE_WEATHER,
+ FEATURE_CALENDAR,
+ FEATURE_COMMUTE_TIME,
+ FEATURE_FLIGHT,
+ FEATURE_TIPS,
+ FEATURE_REMINDER,
+ FEATURE_ALARM,
+ FEATURE_ONBOARDING,
+ FEATURE_SPORTS,
+ FEATURE_WEATHER_ALERT,
+ FEATURE_CONSENT,
+ FEATURE_STOCK_PRICE_CHANGE,
+ FEATURE_SHOPPING_LIST,
+ FEATURE_LOYALTY_CARD,
+ FEATURE_MEDIA,
+ FEATURE_BEDTIME_ROUTINE,
+ FEATURE_FITNESS_TRACKING,
+ FEATURE_ETA_MONITORING,
+ FEATURE_MISSED_CALL,
+ FEATURE_PACKAGE_TRACKING,
+ FEATURE_TIMER,
+ FEATURE_STOPWATCH,
+ FEATURE_UPCOMING_ALARM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FeatureType {
+ }
+
+ private SmartspaceTarget(Parcel in) {
+ this.mSmartspaceTargetId = in.readString();
+ this.mHeaderAction = in.readTypedObject(SmartspaceAction.CREATOR);
+ this.mBaseAction = in.readTypedObject(SmartspaceAction.CREATOR);
+ this.mCreationTimeMillis = in.readLong();
+ this.mExpiryTimeMillis = in.readLong();
+ this.mScore = in.readFloat();
+ this.mActionChips = in.createTypedArrayList(SmartspaceAction.CREATOR);
+ this.mIconGrid = in.createTypedArrayList(SmartspaceAction.CREATOR);
+ this.mFeatureType = in.readInt();
+ this.mSensitive = in.readBoolean();
+ this.mShouldShowExpanded = in.readBoolean();
+ this.mSourceNotificationKey = in.readString();
+ this.mComponentName = in.readTypedObject(ComponentName.CREATOR);
+ this.mUserHandle = in.readTypedObject(UserHandle.CREATOR);
+ this.mAssociatedSmartspaceTargetId = in.readString();
+ this.mSliceUri = in.readTypedObject(Uri.CREATOR);
+ this.mWidgetId = in.readTypedObject(AppWidgetProviderInfo.CREATOR);
+ }
+
+ private SmartspaceTarget(String smartspaceTargetId,
+ SmartspaceAction headerAction, SmartspaceAction baseAction, long creationTimeMillis,
+ long expiryTimeMillis, float score,
+ List<SmartspaceAction> actionChips,
+ List<SmartspaceAction> iconGrid, int featureType, boolean sensitive,
+ boolean shouldShowExpanded, String sourceNotificationKey,
+ ComponentName componentName, UserHandle userHandle,
+ String associatedSmartspaceTargetId, Uri sliceUri,
+ AppWidgetProviderInfo widgetId) {
+ mSmartspaceTargetId = smartspaceTargetId;
+ mHeaderAction = headerAction;
+ mBaseAction = baseAction;
+ mCreationTimeMillis = creationTimeMillis;
+ mExpiryTimeMillis = expiryTimeMillis;
+ mScore = score;
+ mActionChips = actionChips;
+ mIconGrid = iconGrid;
+ mFeatureType = featureType;
+ mSensitive = sensitive;
+ mShouldShowExpanded = shouldShowExpanded;
+ mSourceNotificationKey = sourceNotificationKey;
+ mComponentName = componentName;
+ mUserHandle = userHandle;
+ mAssociatedSmartspaceTargetId = associatedSmartspaceTargetId;
+ mSliceUri = sliceUri;
+ mWidgetId = widgetId;
+ }
+
+ /**
+ * Returns the Id of the target.
+ */
+ @NonNull
+ public String getSmartspaceTargetId() {
+ return mSmartspaceTargetId;
+ }
+
+ /**
+ * Returns the header action of the target.
+ */
+ @Nullable
+ public SmartspaceAction getHeaderAction() {
+ return mHeaderAction;
+ }
+
+ /**
+ * Returns the base action of the target.
+ */
+ @Nullable
+ public SmartspaceAction getBaseAction() {
+ return mBaseAction;
+ }
+
+ /**
+ * Returns the creation time of the target.
+ */
+ @NonNull
+ public long getCreationTimeMillis() {
+ return mCreationTimeMillis;
+ }
+
+ /**
+ * Returns the expiry time of the target.
+ */
+ @NonNull
+ public long getExpiryTimeMillis() {
+ return mExpiryTimeMillis;
+ }
+
+ /**
+ * Returns the score of the target.
+ */
+ @NonNull
+ public float getScore() {
+ return mScore;
+ }
+
+ /**
+ * Return the action chips of the target.
+ */
+ @NonNull
+ public List<SmartspaceAction> getActionChips() {
+ return mActionChips;
+ }
+
+ /**
+ * Return the icons of the target.
+ */
+ @NonNull
+ public List<SmartspaceAction> getIconGrid() {
+ return mIconGrid;
+ }
+
+ /**
+ * Returns the feature type of the target.
+ */
+ @NonNull
+ public int getFeatureType() {
+ return mFeatureType;
+ }
+
+ /**
+ * Returns whether the target is sensitive or not.
+ */
+ @NonNull
+ public boolean isSensitive() {
+ return mSensitive;
+ }
+
+ /**
+ * Returns whether the target should be shown in expanded state.
+ */
+ @NonNull
+ public boolean shouldShowExpanded() {
+ return mShouldShowExpanded;
+ }
+
+ /**
+ * Returns the source notification key of the target.
+ */
+ @Nullable
+ public String getSourceNotificationKey() {
+ return mSourceNotificationKey;
+ }
+
+ /**
+ * Returns the component name of the target.
+ */
+ @NonNull
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
+ * Returns the user handle of the target.
+ */
+ @NonNull
+ public UserHandle getUserHandle() {
+ return mUserHandle;
+ }
+
+ /**
+ * Returns the id of a target associated with this instance.
+ */
+ @Nullable
+ public String getAssociatedSmartspaceTargetId() {
+ return mAssociatedSmartspaceTargetId;
+ }
+
+ /**
+ * Returns the slice uri, if the target is a slice.
+ */
+ @Nullable
+ public Uri getSliceUri() {
+ return mSliceUri;
+ }
+
+ /**
+ * Returns the AppWidgetProviderInfo, if the target is a widget.
+ */
+ @Nullable
+ public AppWidgetProviderInfo getWidgetId() {
+ return mWidgetId;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceTarget> CREATOR = new Creator<SmartspaceTarget>() {
+ @Override
+ public SmartspaceTarget createFromParcel(Parcel source) {
+ return new SmartspaceTarget(source);
+ }
+
+ @Override
+ public SmartspaceTarget[] newArray(int size) {
+ return new SmartspaceTarget[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(this.mSmartspaceTargetId);
+ dest.writeTypedObject(this.mHeaderAction, flags);
+ dest.writeTypedObject(this.mBaseAction, flags);
+ dest.writeLong(this.mCreationTimeMillis);
+ dest.writeLong(this.mExpiryTimeMillis);
+ dest.writeFloat(this.mScore);
+ dest.writeTypedList(this.mActionChips);
+ dest.writeTypedList(this.mIconGrid);
+ dest.writeInt(this.mFeatureType);
+ dest.writeBoolean(this.mSensitive);
+ dest.writeBoolean(this.mShouldShowExpanded);
+ dest.writeString(this.mSourceNotificationKey);
+ dest.writeTypedObject(this.mComponentName, flags);
+ dest.writeTypedObject(this.mUserHandle, flags);
+ dest.writeString(this.mAssociatedSmartspaceTargetId);
+ dest.writeTypedObject(this.mSliceUri, flags);
+ dest.writeTypedObject(this.mWidgetId, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "SmartspaceTarget{"
+ + "mSmartspaceTargetId='" + mSmartspaceTargetId + '\''
+ + ", mHeaderAction=" + mHeaderAction
+ + ", mBaseAction=" + mBaseAction
+ + ", mCreationTimeMillis=" + mCreationTimeMillis
+ + ", mExpiryTimeMillis=" + mExpiryTimeMillis
+ + ", mScore=" + mScore
+ + ", mActionChips=" + mActionChips
+ + ", mIconGrid=" + mIconGrid
+ + ", mFeatureType=" + mFeatureType
+ + ", mSensitive=" + mSensitive
+ + ", mShouldShowExpanded=" + mShouldShowExpanded
+ + ", mSourceNotificationKey='" + mSourceNotificationKey + '\''
+ + ", mComponentName=" + mComponentName
+ + ", mUserHandle=" + mUserHandle
+ + ", mAssociatedSmartspaceTargetId='" + mAssociatedSmartspaceTargetId + '\''
+ + ", mSliceUri=" + mSliceUri
+ + ", mWidgetId=" + mWidgetId
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SmartspaceTarget that = (SmartspaceTarget) o;
+ return mCreationTimeMillis == that.mCreationTimeMillis
+ && mExpiryTimeMillis == that.mExpiryTimeMillis
+ && Float.compare(that.mScore, mScore) == 0
+ && mFeatureType == that.mFeatureType
+ && mSensitive == that.mSensitive
+ && mShouldShowExpanded == that.mShouldShowExpanded
+ && mSmartspaceTargetId.equals(that.mSmartspaceTargetId)
+ && Objects.equals(mHeaderAction, that.mHeaderAction)
+ && Objects.equals(mBaseAction, that.mBaseAction)
+ && Objects.equals(mActionChips, that.mActionChips)
+ && Objects.equals(mIconGrid, that.mIconGrid)
+ && Objects.equals(mSourceNotificationKey, that.mSourceNotificationKey)
+ && mComponentName.equals(that.mComponentName)
+ && mUserHandle.equals(that.mUserHandle)
+ && Objects.equals(mAssociatedSmartspaceTargetId,
+ that.mAssociatedSmartspaceTargetId)
+ && Objects.equals(mSliceUri, that.mSliceUri)
+ && Objects.equals(mWidgetId, that.mWidgetId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSmartspaceTargetId, mHeaderAction, mBaseAction, mCreationTimeMillis,
+ mExpiryTimeMillis, mScore, mActionChips, mIconGrid, mFeatureType, mSensitive,
+ mShouldShowExpanded, mSourceNotificationKey, mComponentName, mUserHandle,
+ mAssociatedSmartspaceTargetId, mSliceUri, mWidgetId);
+ }
+
+ /**
+ * A builder for {@link SmartspaceTarget} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private final String mSmartspaceTargetId;
+ private SmartspaceAction mHeaderAction;
+ private SmartspaceAction mBaseAction;
+ private long mCreationTimeMillis;
+ private long mExpiryTimeMillis;
+ private float mScore;
+ private List<SmartspaceAction> mActionChips = new ArrayList<>();
+ private List<SmartspaceAction> mIconGrid = new ArrayList<>();
+ private int mFeatureType;
+ private boolean mSensitive;
+ private boolean mShouldShowExpanded;
+ private String mSourceNotificationKey;
+ private final ComponentName mComponentName;
+ private final UserHandle mUserHandle;
+ private String mAssociatedSmartspaceTargetId;
+ private Uri mSliceUri;
+ private AppWidgetProviderInfo mWidgetId;
+
+ /**
+ * A builder for {@link SmartspaceTarget}.
+ *
+ * @param smartspaceTargetId the id of this target
+ * @param componentName the componentName of this target
+ * @param userHandle the userHandle of this target
+ */
+ public Builder(@NonNull String smartspaceTargetId,
+ @NonNull ComponentName componentName, @NonNull UserHandle userHandle) {
+ this.mSmartspaceTargetId = smartspaceTargetId;
+ this.mComponentName = componentName;
+ this.mUserHandle = userHandle;
+ }
+
+ /**
+ * Sets the header action.
+ */
+ @NonNull
+ public Builder setHeaderAction(@NonNull SmartspaceAction headerAction) {
+ this.mHeaderAction = headerAction;
+ return this;
+ }
+
+ /**
+ * Sets the base action.
+ */
+ @NonNull
+ public Builder setBaseAction(@NonNull SmartspaceAction baseAction) {
+ this.mBaseAction = baseAction;
+ return this;
+ }
+
+ /**
+ * Sets the creation time.
+ */
+ @NonNull
+ public Builder setCreationTimeMillis(@NonNull long creationTimeMillis) {
+ this.mCreationTimeMillis = creationTimeMillis;
+ return this;
+ }
+
+ /**
+ * Sets the expiration time.
+ */
+ @NonNull
+ public Builder setExpiryTimeMillis(@NonNull long expiryTimeMillis) {
+ this.mExpiryTimeMillis = expiryTimeMillis;
+ return this;
+ }
+
+ /**
+ * Sets the score.
+ */
+ @NonNull
+ public Builder setScore(@NonNull float score) {
+ this.mScore = score;
+ return this;
+ }
+
+ /**
+ * Sets the action chips.
+ */
+ @NonNull
+ public Builder setActionChips(@NonNull List<SmartspaceAction> actionChips) {
+ this.mActionChips = actionChips;
+ return this;
+ }
+
+ /**
+ * Sets the icon grid.
+ */
+ @NonNull
+ public Builder setIconGrid(@NonNull List<SmartspaceAction> iconGrid) {
+ this.mIconGrid = iconGrid;
+ return this;
+ }
+
+ /**
+ * Sets the feature type.
+ */
+ @NonNull
+ public Builder setFeatureType(@NonNull int featureType) {
+ this.mFeatureType = featureType;
+ return this;
+ }
+
+ /**
+ * Sets whether the contents are sensitive.
+ */
+ @NonNull
+ public Builder setSensitive(@NonNull boolean sensitive) {
+ this.mSensitive = sensitive;
+ return this;
+ }
+
+ /**
+ * Sets whether to show the card as expanded.
+ */
+ @NonNull
+ public Builder setShouldShowExpanded(@NonNull boolean shouldShowExpanded) {
+ this.mShouldShowExpanded = shouldShowExpanded;
+ return this;
+ }
+
+ /**
+ * Sets the source notification key.
+ */
+ @NonNull
+ public Builder setSourceNotificationKey(@NonNull String sourceNotificationKey) {
+ this.mSourceNotificationKey = sourceNotificationKey;
+ return this;
+ }
+
+ /**
+ * Sets the associated smartspace target id.
+ */
+ @NonNull
+ public Builder setAssociatedSmartspaceTargetId(
+ @NonNull String associatedSmartspaceTargetId) {
+ this.mAssociatedSmartspaceTargetId = associatedSmartspaceTargetId;
+ return this;
+ }
+
+ /**
+ * Sets the slice uri.
+ *
+ * <p><b>NOTE: </b> If {@link mWidgetId} is also set, {@link mSliceUri} should be ignored.
+ */
+ @NonNull
+ public Builder setSliceUri(@NonNull Uri sliceUri) {
+ this.mSliceUri = sliceUri;
+ return this;
+ }
+
+ /**
+ * Sets the widget id.
+ *
+ * <p><b>NOTE: </b> If {@link mWidgetId} is set, all other @Nullable params should be
+ * ignored.
+ */
+ @NonNull
+ public Builder setWidgetId(@NonNull AppWidgetProviderInfo widgetId) {
+ this.mWidgetId = widgetId;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link SmartspaceTarget}.
+ *
+ * @throws IllegalStateException when non null fields are set as null.
+ */
+ @NonNull
+ public SmartspaceTarget build() {
+ if (mSmartspaceTargetId == null
+ || mComponentName == null
+ || mUserHandle == null) {
+ throw new IllegalStateException("Please assign a value to all @NonNull args.");
+ }
+ return new SmartspaceTarget(mSmartspaceTargetId,
+ mHeaderAction, mBaseAction, mCreationTimeMillis, mExpiryTimeMillis, mScore,
+ mActionChips, mIconGrid, mFeatureType, mSensitive, mShouldShowExpanded,
+ mSourceNotificationKey, mComponentName, mUserHandle,
+ mAssociatedSmartspaceTargetId, mSliceUri, mWidgetId);
+ }
+ }
+}
diff --git a/core/java/android/net/VpnInfo.aidl b/core/java/android/app/smartspace/SmartspaceTargetEvent.aidl
similarity index 74%
copy from core/java/android/net/VpnInfo.aidl
copy to core/java/android/app/smartspace/SmartspaceTargetEvent.aidl
index 8bcaa81..e797a9b 100644
--- a/core/java/android/net/VpnInfo.aidl
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
+/**
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net;
+package android.app.smartspace;
-parcelable VpnInfo;
+parcelable SmartspaceTargetEvent;
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
new file mode 100644
index 0000000..1e0653d
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
@@ -0,0 +1,204 @@
+/*
+ * 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.smartspace;
+
+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;
+
+/**
+ * A representation of a smartspace event.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceTargetEvent implements Parcelable {
+
+ /**
+ * User interacted with the target.
+ */
+ public static final int EVENT_TARGET_INTERACTION = 1;
+
+ /**
+ * Smartspace target was brought into view.
+ */
+ public static final int EVENT_TARGET_IN_VIEW = 2;
+ /**
+ * Smartspace target went out of view.
+ */
+ public static final int EVENT_TARGET_OUT_OF_VIEW = 3;
+ /**
+ * A dismiss action was issued by the user.
+ */
+ public static final int EVENT_TARGET_DISMISS = 4;
+ /**
+ * A block action was issued by the user.
+ */
+ public static final int EVENT_TARGET_BLOCK = 5;
+ /**
+ * The Ui surface came into view.
+ */
+ public static final int EVENT_UI_SURFACE_IN_VIEW = 6;
+ /**
+ * The Ui surface went out of view.
+ */
+ public static final int EVENT_UI_SURFACE_OUT_OF_VIEW = 7;
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceTargetEvent> CREATOR =
+ new Creator<SmartspaceTargetEvent>() {
+ public SmartspaceTargetEvent createFromParcel(Parcel parcel) {
+ return new SmartspaceTargetEvent(parcel);
+ }
+
+ public SmartspaceTargetEvent[] newArray(int size) {
+ return new SmartspaceTargetEvent[size];
+ }
+ };
+
+ @Nullable
+ private final SmartspaceTarget mSmartspaceTarget;
+
+ @Nullable
+ private final String mSmartspaceActionId;
+
+ @EventType
+ private final int mEventType;
+
+ private SmartspaceTargetEvent(@Nullable SmartspaceTarget smartspaceTarget,
+ @Nullable String smartspaceActionId,
+ @EventType int eventType) {
+ mSmartspaceTarget = smartspaceTarget;
+ mSmartspaceActionId = smartspaceActionId;
+ mEventType = eventType;
+ }
+
+ private SmartspaceTargetEvent(Parcel parcel) {
+ mSmartspaceTarget = parcel.readParcelable(null);
+ mSmartspaceActionId = parcel.readString();
+ mEventType = parcel.readInt();
+ }
+
+ /**
+ * Get the {@link SmartspaceTarget} associated with this event.
+ */
+ @Nullable
+ public SmartspaceTarget getSmartspaceTarget() {
+ return mSmartspaceTarget;
+ }
+
+ /**
+ * Get the action id of the Smartspace Action associated with this event.
+ */
+ @Nullable
+ public String getSmartspaceActionId() {
+ return mSmartspaceActionId;
+ }
+
+ /**
+ * Get the {@link EventType} of this event.
+ */
+ @NonNull
+ @EventType
+ public int getEventType() {
+ return mEventType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mSmartspaceTarget, flags);
+ dest.writeString(mSmartspaceActionId);
+ dest.writeInt(mEventType);
+ }
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"EVENT_"}, value = {
+ EVENT_TARGET_INTERACTION,
+ EVENT_TARGET_IN_VIEW,
+ EVENT_TARGET_OUT_OF_VIEW,
+ EVENT_TARGET_DISMISS,
+ EVENT_TARGET_BLOCK,
+ EVENT_UI_SURFACE_IN_VIEW,
+ EVENT_UI_SURFACE_OUT_OF_VIEW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {
+ }
+
+ /**
+ * A builder for {@link SmartspaceTargetEvent}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ @EventType
+ private final int mEventType;
+ @Nullable
+ private SmartspaceTarget mSmartspaceTarget;
+ @Nullable
+ private String mSmartspaceActionId;
+
+ /**
+ * A builder for {@link SmartspaceTargetEvent}.
+ */
+ public Builder(@EventType int eventType) {
+ mEventType = eventType;
+ }
+
+ /**
+ * Sets the SmartspaceTarget for this event.
+ */
+ @NonNull
+ public Builder setSmartspaceTarget(@NonNull SmartspaceTarget smartspaceTarget) {
+ mSmartspaceTarget = smartspaceTarget;
+ return this;
+ }
+
+ /**
+ * Sets the Smartspace action id for this event.
+ */
+ @NonNull
+ public Builder setSmartspaceActionId(@NonNull String smartspaceActionId) {
+ mSmartspaceActionId = smartspaceActionId;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link SmartspaceTargetEvent} instance.
+ */
+ @NonNull
+ public SmartspaceTargetEvent build() {
+ return new SmartspaceTargetEvent(mSmartspaceTarget, mSmartspaceActionId, mEventType);
+ }
+ }
+}
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 1ddfe0d..1d5dc1d 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -28,7 +28,6 @@
import android.net.ConnectivityManager;
import android.net.DataUsageRequest;
import android.net.INetworkStatsService;
-import android.net.NetworkIdentity;
import android.net.NetworkStack;
import android.net.NetworkTemplate;
import android.net.netstats.provider.INetworkStatsProviderCallback;
@@ -47,6 +46,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.NetworkIdentityUtils;
import java.util.Objects;
@@ -628,7 +628,7 @@
default:
throw new IllegalArgumentException("Cannot create template for network type "
+ networkType + ", subscriberId '"
- + NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId) + "'.");
}
return template;
}
diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
index 8f1934c..7281d50 100644
--- a/core/java/android/apphibernation/AppHibernationManager.java
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -49,31 +49,61 @@
}
/**
- * Returns true if the package is hibernating, false otherwise.
+ * Returns true if the package is hibernating for this context's user, false otherwise.
*
* @hide
*/
@SystemApi
- public boolean isHibernating(@NonNull String packageName) {
+ public boolean isHibernatingForUser(@NonNull String packageName) {
try {
- return mIAppHibernationService.isHibernating(packageName, mContext.getUserId());
+ return mIAppHibernationService.isHibernatingForUser(packageName, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Set whether the package is hibernating.
+ * Set whether the package is hibernating for this context's user.
*
* @hide
*/
@SystemApi
- public void setHibernating(@NonNull String packageName, boolean isHibernating) {
+ public void setHibernatingForUser(@NonNull String packageName, boolean isHibernating) {
try {
- mIAppHibernationService.setHibernating(packageName, mContext.getUserId(),
+ mIAppHibernationService.setHibernatingForUser(packageName, mContext.getUserId(),
isHibernating);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns true if app is hibernating globally / at the package level.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isHibernatingGlobally(@NonNull String packageName) {
+ try {
+ return mIAppHibernationService.isHibernatingGlobally(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether a package should be globally hibernating. This hibernates the package at a
+ * package level. User-level hibernation (e.g.. {@link #isHibernatingForUser} is independent
+ * from global hibernation.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setHibernatingGlobally(@NonNull String packageName, boolean isHibernating) {
+ try {
+ mIAppHibernationService.setHibernatingGlobally(packageName, isHibernating);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
index db57ecb..6a068ee 100644
--- a/core/java/android/apphibernation/IAppHibernationService.aidl
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -21,6 +21,8 @@
* @hide
*/
interface IAppHibernationService {
- boolean isHibernating(String packageName, int userId);
- void setHibernating(String packageName, int userId, boolean isHibernating);
+ boolean isHibernatingForUser(String packageName, int userId);
+ void setHibernatingForUser(String packageName, int userId, boolean isHibernating);
+ boolean isHibernatingGlobally(String packageName);
+ void setHibernatingGlobally(String packageName, boolean isHibernating);
}
\ No newline at end of file
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 93d96d0..e96e22c4 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -406,6 +406,14 @@
return new UserHandle(UserHandle.getUserId(providerInfo.applicationInfo.uid));
}
+ /**
+ * Returns the broadcast receiver that is providing this widget.
+ */
+ @NonNull
+ public ActivityInfo getProviderInfo() {
+ return providerInfo;
+ }
+
@Override
@SuppressWarnings("deprecation")
public void writeToParcel(Parcel out, int flags) {
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index d6b38fd..4fb5577 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -80,7 +80,7 @@
/**
* Intent used to broadcast the change in the Audio Connection state of the
- * HDP profile.
+ * HFP profile.
*
* <p>This intent will have 3 extras:
* <ul>
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
index 397326c..cec6580 100644
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ b/core/java/android/bluetooth/le/AdvertiseData.java
@@ -44,7 +44,7 @@
@Nullable
private final List<ParcelUuid> mServiceUuids;
- @Nullable
+ @NonNull
private final List<ParcelUuid> mServiceSolicitationUuids;
private final SparseArray<byte[]> mManufacturerSpecificData;
@@ -77,7 +77,7 @@
/**
* Returns a list of service solicitation UUIDs within the advertisement that we invite to connect.
*/
- @Nullable
+ @NonNull
public List<ParcelUuid> getServiceSolicitationUuids() {
return mServiceSolicitationUuids;
}
@@ -221,7 +221,7 @@
public static final class Builder {
@Nullable
private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
- @Nullable
+ @NonNull
private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>();
private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 083ce96..102c98f 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -55,6 +55,8 @@
genBuilder = false)
public final class AssociationRequest implements Parcelable {
+ private static final String LOG_TAG = AssociationRequest.class.getSimpleName();
+
/**
* Device profile: watch.
*
@@ -115,13 +117,6 @@
mDeviceProfilePrivilegesDescription = desc;
}
- private void onConstructed() {
- if (mDeviceProfile != null
- && !Objects.equals(mDeviceProfile, DEVICE_PROFILE_WATCH)) {
- throw new IllegalArgumentException("Invalid device profile: " + mDeviceProfile);
- }
- }
-
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isSingleDevice() {
@@ -252,7 +247,7 @@
this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
- onConstructed();
+ // onConstructed(); // You can define this method to get a callback
}
/**
@@ -386,7 +381,7 @@
this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
- onConstructed();
+ // onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
@@ -404,10 +399,10 @@
};
@DataClass.Generated(
- time = 1610132130920L,
+ time = 1611692924843L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
+ inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index 80a7b16..0581ed5 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.os.Parcel;
@@ -62,6 +63,7 @@
* List of allowlisted activities.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
/**
@@ -73,6 +75,7 @@
* The disabled Activities of the package. key is component name string, value is when they
* will be enabled.
*/
+ @SuppressLint("NullableCollection")
@Nullable
public ArrayMap<String, Long> disabledActivities;
diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java
index ef49e02..c296bb5 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.os.Parcel;
@@ -73,6 +74,7 @@
* for all acitivites in the package).
*/
@Nullable
+ @SuppressLint("NullableCollection")
public final ArraySet<ComponentName> whitelistedComponents;
/**
@@ -96,6 +98,7 @@
*/
public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs,
int textChangeFlushingFrequencyMs, int logHistorySize,
+ @SuppressLint("NullableCollection")
@Nullable ArraySet<ComponentName> whitelistedComponents) {
this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs,
textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0c50446..4dc41b2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -37,6 +37,7 @@
import android.annotation.UiContext;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.GameManager;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.VrManager;
@@ -4371,6 +4372,16 @@
public static final String BIOMETRIC_SERVICE = "biometric";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.media.MediaCommunicationManager}
+ * for managing {@link android.media.MediaSession2}.
+ *
+ * @see #getSystemService(String)
+ * @see android.media.MediaCommunicationManager
+ */
+ public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
+
+ /**
* Use with {@link #getSystemService} to retrieve a
* {@link android.media.MediaRouter} for controlling and managing
* routing of media.
@@ -4615,6 +4626,18 @@
public static final String SEARCH_UI_SERVICE = "search_ui";
/**
+ * Used for getting the smartspace service.
+ *
+ * <p><b>NOTE: </b> this service is optional; callers of
+ * {@code Context.getSystemServiceName(SMARTSPACE_SERVICE)} should check for {@code null}.
+ *
+ * @hide
+ * @see #getSystemService(String)
+ */
+ @SystemApi
+ public static final String SMARTSPACE_SERVICE = "smartspace";
+
+ /**
* Use with {@link #getSystemService(String)} to access the
* {@link com.android.server.voiceinteraction.SoundTriggerService}.
*
@@ -5418,6 +5441,16 @@
public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link GameManager}.
+ *
+ * @see #getSystemService(String)
+ *
+ * @hide
+ */
+ public static final String GAME_SERVICE = "game";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
@@ -6134,6 +6167,7 @@
@UiContext
@NonNull
public Context createWindowContext(@NonNull Display display, @WindowType int type,
+ @SuppressLint("NullableCollection")
@Nullable Bundle options) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 82d7b63..16e720e 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -32,8 +33,6 @@
* and badged icon for the activity.
*/
public class LauncherActivityInfo {
- private static final String TAG = "LauncherActivityInfo";
-
private final PackageManager mPm;
private UserHandle mUser;
private final LauncherActivityInfoInternal mInternal;
@@ -81,7 +80,7 @@
*/
public CharSequence getLabel() {
// TODO: Go through LauncherAppsService
- return mInternal.getActivityInfo().loadLabel(mPm);
+ return getActivityInfo().loadLabel(mPm);
}
/**
@@ -101,20 +100,20 @@
*/
public Drawable getIcon(int density) {
// TODO: Go through LauncherAppsService
- final int iconRes = mInternal.getActivityInfo().getIconResource();
+ final int iconRes = getActivityInfo().getIconResource();
Drawable icon = null;
// Get the preferred density icon from the app's resources
if (density != 0 && iconRes != 0) {
try {
final Resources resources = mPm.getResourcesForApplication(
- mInternal.getActivityInfo().applicationInfo);
+ getActivityInfo().applicationInfo);
icon = resources.getDrawableForDensity(iconRes, density);
} catch (NameNotFoundException | Resources.NotFoundException exc) {
}
}
// Get the default density icon
if (icon == null) {
- icon = mInternal.getActivityInfo().loadIcon(mPm);
+ icon = getActivityInfo().loadIcon(mPm);
}
return icon;
}
@@ -126,25 +125,25 @@
* @hide remove before shipping
*/
public int getApplicationFlags() {
- return mInternal.getActivityInfo().flags;
+ return getActivityInfo().flags;
}
/**
* Returns the ActivityInfo of the activity.
*
* @return Activity Info
- * @hide
*/
+ @NonNull
public ActivityInfo getActivityInfo() {
return mInternal.getActivityInfo();
}
/**
- * Returns the application info for the appliction this activity belongs to.
+ * Returns the application info for the application this activity belongs to.
* @return
*/
public ApplicationInfo getApplicationInfo() {
- return mInternal.getActivityInfo().applicationInfo;
+ return getActivityInfo().applicationInfo;
}
/**
@@ -155,7 +154,7 @@
public long getFirstInstallTime() {
try {
// TODO: Go through LauncherAppsService
- return mPm.getPackageInfo(mInternal.getActivityInfo().packageName,
+ return mPm.getPackageInfo(getActivityInfo().packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES).firstInstallTime;
} catch (NameNotFoundException nnfe) {
// Sorry, can't find package
@@ -164,11 +163,11 @@
}
/**
- * Returns the name for the acitivty from android:name in the manifest.
- * @return the name from android:name for the acitivity.
+ * Returns the name for the activity from android:name in the manifest.
+ * @return the name from android:name for the activity.
*/
public String getName() {
- return mInternal.getActivityInfo().name;
+ return getActivityInfo().name;
}
/**
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index f991306..e8ef077 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -222,7 +222,7 @@
* <attribution>} tags included under <manifest>, or null if there were none. This
* is only filled if the flag {@link PackageManager#GET_ATTRIBUTIONS} was set.
*/
- @SuppressWarnings("ArrayReturn")
+ @SuppressWarnings({"ArrayReturn", "NullableCollection"})
public @Nullable Attribution[] attributions;
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9ae9c25..0e7e6da 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -48,17 +48,13 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.dex.ArtManager;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
@@ -74,6 +70,12 @@
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.permission.PermissionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.gba.GbaService;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.SipDelegateManager;
import android.util.AndroidException;
import android.util.Log;
@@ -82,7 +84,6 @@
import dalvik.system.VMRuntime;
-import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.cert.Certificate;
@@ -124,6 +125,19 @@
}
/**
+ * <application> level {@link android.content.pm.PackageManager.Property} tag specifying
+ * the XML resource ID containing an application's media capabilities XML file
+ *
+ * For example:
+ * <application>
+ * <property android:name="android.media.PROPERTY_MEDIA_CAPABILITIES"
+ * android:resource="@xml/media_capabilities">
+ * <application>
+ */
+ public static final String PROPERTY_MEDIA_CAPABILITIES =
+ "android.media.PROPERTY_MEDIA_CAPABILITIES";
+
+ /**
* A property value set within the manifest.
* <p>
* The value of a property will only have a single type, as defined by
@@ -2928,6 +2942,37 @@
public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * supports a single IMS registration as defined by carrier networks in the IMS service
+ * implementation using the {@link ImsService} API, {@link GbaService} API, and IRadio 1.6 HAL.
+ * <p>
+ * When set, the device must fully support the following APIs for an application to implement
+ * IMS single registration:
+ * <ul>
+ * <li> Updating RCS provisioning status using the {@link ProvisioningManager} API to supply an
+ * RCC.14 defined XML and notify IMS applications of Auto Configuration Server (ACS) or
+ * proprietary server provisioning updates.</li>
+ * <li>Opening a delegate in the device IMS service to forward SIP traffic to the carrier's
+ * network using the {@link SipDelegateManager} API</li>
+ * <li>Listening to EPS dedicated bearer establishment via the
+ * {@link ConnectivityManager#registerQosCallback}
+ * API to indicate to the application when to start/stop media traffic.</li>
+ * <li>Implementing Generic Bootstrapping Architecture (GBA) and providing the associated
+ * authentication keys to applications
+ * requesting this information via the {@link TelephonyManager#bootstrapAuthenticationRequest}
+ * API</li>
+ * <li>Implementing RCS User Capability Exchange using the {@link RcsUceAdapter} API</li>
+ * </ul>
+ * <p>
+ * This feature should only be defined if {@link #FEATURE_TELEPHONY_IMS} is also defined.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION =
+ "android.hardware.telephony.ims.singlereg";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device is capable of communicating with
* other devices via ultra wideband.
@@ -3581,6 +3626,24 @@
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * a Keystore implementation that can only enforce limited use key in hardware with max usage
+ * count equals to 1.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY =
+ "android.hardware.keystore.single_use_key";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * a Keystore implementation that can enforce limited use key in hardware with any max usage
+ * count (including count equals to 1).
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY =
+ "android.hardware.keystore.limited_use_key";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
@@ -5489,7 +5552,7 @@
*
* @hide
*/
- @SuppressWarnings("HiddenAbstractMethod")
+ @SuppressWarnings({"HiddenAbstractMethod", "NullableCollection"})
@TestApi
public abstract @Nullable String[] getNamesForUids(int[] uids);
@@ -6703,6 +6766,22 @@
throws NameNotFoundException;
/**
+ * Retrieve the resources for an application for the provided configuration.
+ *
+ * @param app Information about the desired application.
+ * @param configuration Overridden configuration when loading the Resources
+ *
+ * @return Returns the application's Resources.
+ * @throws NameNotFoundException Thrown if the resources for the given
+ * application could not be loaded (most likely because it was uninstalled).
+ */
+ @NonNull
+ public Resources getResourcesForApplication(@NonNull ApplicationInfo app, @Nullable
+ Configuration configuration) throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Retrieve the resources associated with an application. Given the full
* package name of an application, retrieves the information about it and
* calls getResources() to return its application's resources. If the
@@ -6761,25 +6840,8 @@
@Nullable
public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
@PackageInfoFlags int flags) {
- if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
- // Caller expressed no opinion about what encryption
- // aware/unaware components they want to see, so match both
- flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- }
-
- boolean collectCertificates = (flags & PackageManager.GET_SIGNATURES) != 0
- || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
-
- ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
- ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
- new File(archiveFilePath), 0, collectCertificates);
- if (result.isError()) {
- return null;
- }
- return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flags, 0, 0, null,
- new PackageUserState(), UserHandle.getCallingUserId());
+ throw new UnsupportedOperationException(
+ "getPackageArchiveInfo() not implemented in subclass");
}
/**
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index 02fb06b..bce4b87 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -256,6 +256,8 @@
try {
if (obj != null) {
Signature other = (Signature)obj;
+ // Note, some classes, such as PackageParser.SigningDetails, rely on equals
+ // only comparing the mSignature arrays without the flags.
return this == other || Arrays.equals(mSignature, other.mSignature);
}
} catch (ClassCastException e) {
@@ -268,6 +270,8 @@
if (mHaveHashCode) {
return mHashCode;
}
+ // Note, similar to equals some classes rely on the hash code not including
+ // the flags for Set membership checks.
mHashCode = Arrays.hashCode(mSignature);
mHaveHashCode = true;
return mHashCode;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index b054304..8fbf287 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -34,7 +34,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleableRes;
-import android.app.ActivityThread;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -222,13 +221,15 @@
private static final int MAX_FILE_NAME_SIZE = 223;
/**
- * @see #parseDefault(ParseInput, File, int, boolean)
+ * @see #parseDefault(ParseInput, File, int, List, boolean)
*/
@NonNull
public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
- @ParseFlags int parseFlags, boolean collectCertificates) {
+ @ParseFlags int parseFlags,
+ @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+ boolean collectCertificates) {
ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
- return parseDefault(input, file, parseFlags, collectCertificates);
+ return parseDefault(input, file, parseFlags, splitPermissions, collectCertificates);
}
/**
@@ -238,28 +239,32 @@
*/
@NonNull
public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file,
- @ParseFlags int parseFlags, boolean collectCertificates) {
+ @ParseFlags int parseFlags,
+ @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+ boolean collectCertificates) {
ParseResult<ParsingPackage> result;
- ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, new Callback() {
- @Override
- public boolean hasFeature(String feature) {
- // Assume the device doesn't support anything. This will affect permission parsing
- // and will force <uses-permission/> declarations to include all requiredNotFeature
- // permissions and exclude all requiredFeature permissions. This mirrors the old
- // behavior.
- return false;
- }
+ ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, splitPermissions,
+ new Callback() {
+ @Override
+ public boolean hasFeature(String feature) {
+ // Assume the device doesn't support anything. This will affect permission
+ // parsing and will force <uses-permission/> declarations to include all
+ // requiredNotFeature permissions and exclude all requiredFeature
+ // permissions. This mirrors the old behavior.
+ return false;
+ }
- @Override
- public ParsingPackage startParsingPackage(
- @NonNull String packageName,
- @NonNull String baseApkPath,
- @NonNull String path,
- @NonNull TypedArray manifestArray, boolean isCoreApp) {
- return new ParsingPackageImpl(packageName, baseApkPath, path, manifestArray);
- }
- });
+ @Override
+ public ParsingPackage startParsingPackage(
+ @NonNull String packageName,
+ @NonNull String baseApkPath,
+ @NonNull String path,
+ @NonNull TypedArray manifestArray, boolean isCoreApp) {
+ return new ParsingPackageImpl(packageName, baseApkPath, path,
+ manifestArray);
+ }
+ });
try {
result = parser.parsePackage(input, file, parseFlags);
if (result.isError()) {
@@ -290,13 +295,18 @@
private boolean mOnlyCoreApps;
private String[] mSeparateProcesses;
private DisplayMetrics mDisplayMetrics;
+ @NonNull
+ private List<PermissionManager.SplitPermissionInfo> mSplitPermissionInfos;
private Callback mCallback;
public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses,
- DisplayMetrics displayMetrics, @NonNull Callback callback) {
+ DisplayMetrics displayMetrics,
+ @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+ @NonNull Callback callback) {
mOnlyCoreApps = onlyCoreApps;
mSeparateProcesses = separateProcesses;
mDisplayMetrics = displayMetrics;
+ mSplitPermissionInfos = splitPermissions;
mCallback = callback;
}
@@ -2743,14 +2753,10 @@
}
}
- private static void convertSplitPermissions(ParsingPackage pkg) {
- final List<PermissionManager.SplitPermissionInfo> splitPermissions =
- ActivityThread.currentApplication().getSystemService(PermissionManager.class)
- .getSplitPermissions();
-
- final int listSize = splitPermissions.size();
+ private void convertSplitPermissions(ParsingPackage pkg) {
+ final int listSize = mSplitPermissionInfos.size();
for (int is = 0; is < listSize; is++) {
- final PermissionManager.SplitPermissionInfo spi = splitPermissions.get(is);
+ final PermissionManager.SplitPermissionInfo spi = mSplitPermissionInfos.get(is);
List<String> requestedPermissions = pkg.getRequestedPermissions();
if (pkg.getTargetSdkVersion() >= spi.getTargetSdk()
|| !requestedPermissions.contains(spi.getSplitPermission())) {
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index f6edb2e..abf694f 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -89,6 +89,11 @@
private static final int NEEDS_COMPAT_RES = 16;
/**
+ * Set if the application needs to be forcibly downscaled
+ */
+ private static final int HAS_OVERRIDE_SCALING = 32;
+
+ /**
* The effective screen density we have selected for this application.
*/
public final int applicationDensity;
@@ -107,6 +112,11 @@
@UnsupportedAppUsage
public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
boolean forceCompat) {
+ this(appInfo, screenLayout, sw, forceCompat, 1f);
+ }
+
+ public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
+ boolean forceCompat, float overrideScale) {
int compatFlags = 0;
if (appInfo.targetSdkVersion < VERSION_CODES.O) {
@@ -241,7 +251,12 @@
compatFlags |= NEVER_NEEDS_COMPAT;
}
- if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+ if (overrideScale != 1.0f) {
+ applicationDensity = DisplayMetrics.DENSITY_DEFAULT;
+ applicationScale = overrideScale;
+ applicationInvertedScale = 1.0f / overrideScale;
+ compatFlags |= HAS_OVERRIDE_SCALING;
+ } else if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
applicationDensity = DisplayMetrics.DENSITY_DEVICE;
applicationScale = 1.0f;
applicationInvertedScale = 1.0f;
@@ -277,7 +292,7 @@
*/
@UnsupportedAppUsage
public boolean isScalingRequired() {
- return (mCompatibilityFlags&SCALING_REQUIRED) != 0;
+ return (mCompatibilityFlags & (SCALING_REQUIRED | HAS_OVERRIDE_SCALING)) != 0;
}
@UnsupportedAppUsage
@@ -303,7 +318,7 @@
*/
@UnsupportedAppUsage
public Translator getTranslator() {
- return isScalingRequired() ? new Translator() : null;
+ return (mCompatibilityFlags & SCALING_REQUIRED) != 0 ? new Translator() : null;
}
/**
@@ -504,6 +519,16 @@
if (isScalingRequired()) {
float invertedRatio = applicationInvertedScale;
inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f);
+ inoutConfig.screenWidthDp = (int) ((inoutConfig.screenWidthDp * invertedRatio) + .5f);
+ inoutConfig.screenHeightDp = (int) ((inoutConfig.screenHeightDp * invertedRatio) + .5f);
+ inoutConfig.smallestScreenWidthDp =
+ (int) ((inoutConfig.smallestScreenWidthDp * invertedRatio) + .5f);
+ inoutConfig.windowConfiguration.getMaxBounds().scale(invertedRatio);
+ inoutConfig.windowConfiguration.getBounds().scale(invertedRatio);
+ final Rect appBounds = inoutConfig.windowConfiguration.getAppBounds();
+ if (appBounds != null) {
+ appBounds.scale(invertedRatio);
+ }
}
}
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index eca56b3..f0b218c 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -16,13 +16,17 @@
package android.graphics.fonts;
+import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.text.FontConfig;
import android.util.Log;
@@ -35,6 +39,10 @@
/**
* This class gives you control of system installed font files.
+ *
+ * <p>
+ * This class gives you the information of system font configuration and ability of changing them.
+ *
* @hide
*/
@SystemApi
@@ -45,68 +53,87 @@
private final @NonNull IFontManager mIFontManager;
/** @hide */
- @IntDef(prefix = "ERROR_CODE_",
- value = { ERROR_CODE_OK, ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
- ERROR_CODE_VERIFICATION_FAILURE, ERROR_CODE_FONT_NAME_MISMATCH,
- ERROR_CODE_INVALID_FONT_FILE, ERROR_CODE_MISSING_POST_SCRIPT_NAME,
- ERROR_CODE_DOWNGRADING, ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
- ERROR_CODE_FONT_UPDATER_DISABLED })
+ @IntDef(prefix = "RESULT_",
+ value = { RESULT_SUCCESS, RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ RESULT_ERROR_VERIFICATION_FAILURE, RESULT_ERROR_VERSION_MISMATCH,
+ RESULT_ERROR_INVALID_FONT_FILE, RESULT_ERROR_INVALID_FONT_NAME,
+ RESULT_ERROR_DOWNGRADING, RESULT_ERROR_FAILED_UPDATE_CONFIG,
+ RESULT_ERROR_FONT_UPDATER_DISABLED, RESULT_ERROR_REMOTE_EXCEPTION })
@Retention(RetentionPolicy.SOURCE)
- public @interface ErrorCode {}
+ public @interface ResultCode {}
/**
- * Indicates an operation has processed successfully.
- * @hide
+ * Indicates that the request has been processed successfully.
*/
- public static final int ERROR_CODE_OK = 0;
+ public static final int RESULT_SUCCESS = 0;
/**
- * Indicates a failure of writing font files.
- * @hide
+ * Indicates that a failure occurred while writing the font file to disk.
+ *
+ * This is an internal error that the system cannot place the font file for being used by
+ * application.
*/
- public static final int ERROR_CODE_FAILED_TO_WRITE_FONT_FILE = -1;
+ public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1;
/**
- * Indicates a failure of fs-verity setup.
- * @hide
+ * Indicates that a failure occurred during the verification of the font file.
+ *
+ * The system failed to verify given font file contents and signature with system installed
+ * certificate.
*/
- public static final int ERROR_CODE_VERIFICATION_FAILURE = -2;
+ public static final int RESULT_ERROR_VERIFICATION_FAILURE = -2;
/**
- * Indicates a failure of verifying the font name with PostScript name.
- * @hide
+ * Indicates that a failure occurred as a result of invalid font format or content.
+ *
+ * Android only accepts OpenType compliant font files.
*/
- public static final int ERROR_CODE_FONT_NAME_MISMATCH = -3;
+ public static final int RESULT_ERROR_INVALID_FONT_FILE = -3;
/**
- * Indicates a failure of placing fonts due to unexpected font contents.
- * @hide
+ * Indicates a failure due to missing PostScript name in font's name table.
+ *
+ * Indicates that a failure occurred since PostScript name in the name table(ID=6) was missing.
+ * The font is expected to have a PostScript name.
*/
- public static final int ERROR_CODE_INVALID_FONT_FILE = -4;
+ public static final int RESULT_ERROR_INVALID_FONT_NAME = -4;
/**
- * Indicates a failure due to missing PostScript name in name table.
- * @hide
+ * Indicates that a failure occurred due to downgrading the font version.
+ *
+ * The font must have equal or newer revision in its head table.
*/
- public static final int ERROR_CODE_MISSING_POST_SCRIPT_NAME = -5;
+ public static final int RESULT_ERROR_DOWNGRADING = -5;
/**
- * Indicates a failure of placing fonts due to downgrading.
- * @hide
+ * Indicates that a failure occurred while updating system font configuration.
+ *
+ * This is an internal error that the system couldn't update the {@link FontConfig}.
*/
- public static final int ERROR_CODE_DOWNGRADING = -6;
-
- /**
- * Indicates a failure of writing system font configuration XML file.
- * @hide
- */
- public static final int ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE = -7;
+ public static final int RESULT_ERROR_FAILED_UPDATE_CONFIG = -6;
/**
* Indicates a failure due to disabled font updater.
- * @hide
+ *
+ * This is typically returned due to missing Linux kernel feature.
+ * The font updater only works with the Linux kernel that has fs-verity feature. The fs-verity
+ * is required after the device shipped with Android 11. Thus the updated device may not have
+ * fs-verity feature and font updater is disabled.
*/
- public static final int ERROR_CODE_FONT_UPDATER_DISABLED = -8;
+ public static final int RESULT_ERROR_FONT_UPDATER_DISABLED = -7;
+
+ /**
+ * Indicates that a failure occurred because provided {@code baseVersion} did not match.
+ *
+ * The {@code baseVersion} provided does not match to the current {@link FontConfig} version.
+ * Please get the latest configuration and update {@code baseVersion} accordingly.
+ */
+ public static final int RESULT_ERROR_VERSION_MISMATCH = -8;
+
+ /**
+ * Indicates a failure due to IPC communication.
+ */
+ public static final int RESULT_ERROR_REMOTE_EXCEPTION = -9;
/**
* Indicates a failure of opening font file.
@@ -115,7 +142,7 @@
*
* @hide
*/
- public static final int ERROR_CODE_FAILED_TO_OPEN_FONT_FILE = -10001;
+ public static final int RESULT_ERROR_FAILED_TO_OPEN_FONT_FILE = -10001;
/**
* Indicates a failure of opening signature file.
@@ -124,7 +151,7 @@
*
* @hide
*/
- public static final int ERROR_CODE_FAILED_TO_OPEN_SIGNATURE_FILE = -10002;
+ public static final int RESULT_ERROR_FAILED_TO_OPEN_SIGNATURE_FILE = -10002;
/**
* Indicates a failure of invalid shell command arguments.
@@ -133,7 +160,7 @@
*
* @hide
*/
- public static final int ERROR_CODE_INVALID_SHELL_ARGUMENT = -10003;
+ public static final int RESULT_ERROR_INVALID_SHELL_ARGUMENT = -10003;
/**
* Indicates a failure of reading signature file.
@@ -142,7 +169,7 @@
*
* @hide
*/
- public static final int ERROR_CODE_INVALID_SIGNATURE_FILE = -10004;
+ public static final int RESULT_ERROR_INVALID_SIGNATURE_FILE = -10004;
/**
* Indicates a failure due to exceeding allowed signature file size (8kb).
@@ -151,7 +178,7 @@
*
* @hide
*/
- public static final int ERROR_CODE_SIGNATURE_TOO_LARGE = -10005;
+ public static final int RESULT_ERROR_SIGNATURE_TOO_LARGE = -10005;
private FontManager(@NonNull IFontManager iFontManager) {
@@ -178,6 +205,70 @@
}
/**
+ * Update system installed font file.
+ *
+ * <p>
+ * To protect devices, system font updater relies on the Linux Kernel feature called fs-verity.
+ * If the device is not ready for fs-verity, {@link #RESULT_ERROR_FONT_UPDATER_DISABLED} will be
+ * returned.
+ *
+ * Android only accepts OpenType compliant font files. If other font files are provided,
+ * {@link #RESULT_ERROR_INVALID_FONT_FILE} will be returned.
+ *
+ * The font file to be updated is identified by PostScript name stored in name table. If the
+ * font file doesn't have PostScript name entry, {@link #RESULT_ERROR_INVALID_FONT_NAME} will be
+ * returned.
+ *
+ * The entire font file is verified with the given signature for the system installed
+ * certificate. If the system cannot verify the font contents,
+ * {@link #RESULT_ERROR_VERIFICATION_FAILURE} will be returned.
+ *
+ * The font file must have newer or equal revision number in the head table. In other words, the
+ * downgrading font file is not allowed. If the older font file is provided,
+ * {@link #RESULT_ERROR_DOWNGRADING} will be returned.
+ *
+ * The caller must specify the base config version for keeping consist system configuration. If
+ * the system configuration is updated for some reason between you get config with
+ * {@link #getFontConfig()} and calling this method, {@link #RESULT_ERROR_VERSION_MISMATCH} will
+ * be returned. Get the latest font configuration by calling {@link #getFontConfig()} again and
+ * try with the latest config version again.
+ *
+ * @param pfd A file descriptor of the font file.
+ * @param signature A PKCS#7 detached signature for verifying entire font files.
+ * @param baseVersion A base config version to be updated. You can get latest config version by
+ * {@link FontConfig#getConfigVersion()} via {@link #getFontConfig()}. If the
+ * system has newer config version, the update will fail with
+ * {@link #RESULT_ERROR_VERSION_MISMATCH}. Try to get the latest config and
+ * try update again.
+ * @return result code.
+ *
+ * @see FontConfig#getConfigVersion()
+ * @see #getFontConfig()
+ * @see #RESULT_SUCCESS
+ * @see #RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE
+ * @see #RESULT_ERROR_VERIFICATION_FAILURE
+ * @see #RESULT_ERROR_VERSION_MISMATCH
+ * @see #RESULT_ERROR_INVALID_FONT_FILE
+ * @see #RESULT_ERROR_INVALID_FONT_NAME
+ * @see #RESULT_ERROR_DOWNGRADING
+ * @see #RESULT_ERROR_FAILED_UPDATE_CONFIG
+ * @see #RESULT_ERROR_FONT_UPDATER_DISABLED
+ * @see #RESULT_ERROR_REMOTE_EXCEPTION
+ */
+ @RequiresPermission(Manifest.permission.UPDATE_FONTS) public @ResultCode int updateFontFile(
+ @NonNull ParcelFileDescriptor pfd,
+ @NonNull byte[] signature,
+ @IntRange(from = 0) int baseVersion
+ ) {
+ try {
+ return mIFontManager.updateFont(pfd, signature, baseVersion);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call updateFont API", e);
+ return RESULT_ERROR_REMOTE_EXCEPTION;
+ }
+ }
+
+ /**
* Factory method of the FontManager.
*
* Do not use this method directly. Use getSystemService(Context.FONT_SERVICE) instead.
diff --git a/core/java/android/hardware/Battery.java b/core/java/android/hardware/Battery.java
new file mode 100644
index 0000000..24c8d76
--- /dev/null
+++ b/core/java/android/hardware/Battery.java
@@ -0,0 +1,74 @@
+/*
+ * 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;
+
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.os.BatteryManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The Battery class is a representation of a single battery on a device.
+ */
+public abstract class Battery {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_UNKNOWN,
+ STATUS_CHARGING,
+ STATUS_DISCHARGING,
+ STATUS_NOT_CHARGING,
+ STATUS_FULL
+ })
+ public @interface BatteryStatus {
+ }
+
+ /** Battery status is unknown. */
+ public static final int STATUS_UNKNOWN = BatteryManager.BATTERY_STATUS_UNKNOWN;
+ /** Battery is charging. */
+ public static final int STATUS_CHARGING = BatteryManager.BATTERY_STATUS_CHARGING;
+ /** Battery is discharging. */
+ public static final int STATUS_DISCHARGING = BatteryManager.BATTERY_STATUS_DISCHARGING;
+ /** Battery is connected to power but not charging. */
+ public static final int STATUS_NOT_CHARGING = BatteryManager.BATTERY_STATUS_NOT_CHARGING;
+ /** Battery is full. */
+ public static final int STATUS_FULL = BatteryManager.BATTERY_STATUS_FULL;
+
+ /**
+ * Check whether the hardware has a battery.
+ *
+ * @return True if the hardware has a battery, else false.
+ */
+ public abstract boolean hasBattery();
+
+ /**
+ * Get the battery status.
+ *
+ * @return the battery status.
+ */
+ public abstract @BatteryStatus int getStatus();
+
+ /**
+ * Get remaining battery capacity as float percentage [0.0f, 1.0f] of total capacity
+ * Returns -1 when battery capacity can't be read.
+ *
+ * @return the battery capacity.
+ */
+ public abstract @FloatRange(from = -1.0f, to = 1.0f) float getCapacity();
+}
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index e2d836c..a6c6b46 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -65,6 +65,23 @@
/** If night mode color filter is active this will be the temperature in kelvin */
public final int colorTemperature;
+ /** Whether the bright color reduction color transform is active */
+ public final boolean reduceBrightColors;
+
+ /** How strong the bright color reduction color transform is set (only applicable if active),
+ * specified as an integer from 0 - 100, inclusive. This value (scaled to 0-1, inclusive) is
+ * then used in Ynew = (a * scaledStrength^2 + b * scaledStrength + c) * Ycurrent, where a, b,
+ * and c are coefficients provided in the bright color reduction coefficient matrix, and
+ * Ycurrent is the current hardware brightness in nits.
+ */
+ public final int reduceBrightColorsStrength;
+
+ /** Applied offset for the bright color reduction color transform (only applicable if active).
+ * The offset is computed by summing the coefficients a, b, and c, from the coefficient matrix
+ * and multiplying by the current brightness.
+ */
+ public final float reduceBrightColorsOffset;
+
/** Brightness level before slider adjustment */
public final float lastBrightness;
@@ -105,8 +122,9 @@
private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
float powerBrightnessFactor, boolean nightMode, int colorTemperature,
- float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness,
- long[] colorValueBuckets, long colorSampleDuration) {
+ boolean reduceBrightColors, int reduceBrightColorsStrength,
+ float reduceBrightColorsOffset, float lastBrightness, boolean isDefaultBrightnessConfig,
+ boolean isUserSetBrightness, long[] colorValueBuckets, long colorSampleDuration) {
this.brightness = brightness;
this.timeStamp = timeStamp;
this.packageName = packageName;
@@ -117,6 +135,9 @@
this.powerBrightnessFactor = powerBrightnessFactor;
this.nightMode = nightMode;
this.colorTemperature = colorTemperature;
+ this.reduceBrightColors = reduceBrightColors;
+ this.reduceBrightColorsStrength = reduceBrightColorsStrength;
+ this.reduceBrightColorsOffset = reduceBrightColorsOffset;
this.lastBrightness = lastBrightness;
this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
this.isUserSetBrightness = isUserSetBrightness;
@@ -136,6 +157,9 @@
this.powerBrightnessFactor = other.powerBrightnessFactor;
this.nightMode = other.nightMode;
this.colorTemperature = other.colorTemperature;
+ this.reduceBrightColors = other.reduceBrightColors;
+ this.reduceBrightColorsStrength = other.reduceBrightColorsStrength;
+ this.reduceBrightColorsOffset = other.reduceBrightColorsOffset;
this.lastBrightness = other.lastBrightness;
this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
this.isUserSetBrightness = other.isUserSetBrightness;
@@ -154,6 +178,9 @@
powerBrightnessFactor = source.readFloat();
nightMode = source.readBoolean();
colorTemperature = source.readInt();
+ reduceBrightColors = source.readBoolean();
+ reduceBrightColorsStrength = source.readInt();
+ reduceBrightColorsOffset = source.readFloat();
lastBrightness = source.readFloat();
isDefaultBrightnessConfig = source.readBoolean();
isUserSetBrightness = source.readBoolean();
@@ -188,6 +215,9 @@
dest.writeFloat(powerBrightnessFactor);
dest.writeBoolean(nightMode);
dest.writeInt(colorTemperature);
+ dest.writeBoolean(reduceBrightColors);
+ dest.writeInt(reduceBrightColorsStrength);
+ dest.writeFloat(reduceBrightColorsOffset);
dest.writeFloat(lastBrightness);
dest.writeBoolean(isDefaultBrightnessConfig);
dest.writeBoolean(isUserSetBrightness);
@@ -207,6 +237,9 @@
private float mPowerBrightnessFactor;
private boolean mNightMode;
private int mColorTemperature;
+ private boolean mReduceBrightColors;
+ private int mReduceBrightColorsStrength;
+ private float mReduceBrightColorsOffset;
private float mLastBrightness;
private boolean mIsDefaultBrightnessConfig;
private boolean mIsUserSetBrightness;
@@ -273,6 +306,24 @@
return this;
}
+ /** {@see BrightnessChangeEvent#reduceBrightColors} */
+ public Builder setReduceBrightColors(boolean reduceBrightColors) {
+ mReduceBrightColors = reduceBrightColors;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#reduceBrightColorsStrength} */
+ public Builder setReduceBrightColorsStrength(int strength) {
+ mReduceBrightColorsStrength = strength;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#reduceBrightColorsOffset} */
+ public Builder setReduceBrightColorsOffset(float offset) {
+ mReduceBrightColorsOffset = offset;
+ return this;
+ }
+
/** {@see BrightnessChangeEvent#lastBrightness} */
public Builder setLastBrightness(float lastBrightness) {
mLastBrightness = lastBrightness;
@@ -304,7 +355,8 @@
public BrightnessChangeEvent build() {
return new BrightnessChangeEvent(mBrightness, mTimeStamp,
mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
- mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
+ mPowerBrightnessFactor, mNightMode, mColorTemperature, mReduceBrightColors,
+ mReduceBrightColorsStrength, mReduceBrightColorsOffset, mLastBrightness,
mIsDefaultBrightnessConfig, mIsUserSetBrightness, mColorValueBuckets,
mColorSampleDuration);
}
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index efeb89e..e247df3 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -438,6 +438,56 @@
}
/**
+ * Enables or disables reduce bright colors.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setReduceBrightColorsActivated(boolean activated) {
+ return mManager.setReduceBrightColorsActivated(activated);
+ }
+
+ /**
+ * Returns whether reduce bright colors is currently enabled.
+ *
+ * @hide
+ */
+ public boolean isReduceBrightColorsActivated() {
+ return mManager.isReduceBrightColorsActivated();
+ }
+
+ /**
+ * Set the strength level of bright color reduction to apply to the display.
+ *
+ * @param strength 0-100 (inclusive), where 100 is full strength
+ * @return whether the change was applied successfully
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setReduceBrightColorsStrength(@IntRange(from = 0, to = 100) int strength) {
+ return mManager.setReduceBrightColorsStrength(strength);
+ }
+
+ /**
+ * Gets the strength of the bright color reduction transform.
+ *
+ * @hide
+ */
+ public int getReduceBrightColorsStrength() {
+ return mManager.getReduceBrightColorsStrength();
+ }
+
+ /**
+ * Gets the brightness impact of the bright color reduction transform, as in the factor by which
+ * the current brightness (in nits) should be multiplied to obtain the brightness offset 'b'.
+ *
+ * @hide
+ */
+ public float getReduceBrightColorsOffsetFactor() {
+ return mManager.getReduceBrightColorsOffsetFactor();
+ }
+
+ /**
* Returns {@code true} if Night Display is supported by the device.
*
* @hide
@@ -478,6 +528,15 @@
}
/**
+ * Returns {@code true} if reduce bright colors is supported by the device.
+ *
+ * @hide
+ */
+ public static boolean isReduceBrightColorsAvailable(Context context) {
+ return context.getResources().getBoolean(R.bool.config_reduceBrightColorsAvailable);
+ }
+
+ /**
* Check if the color transforms are color accelerated. Some transforms are experimental only
* on non-accelerated platforms due to the performance implications.
*
@@ -678,6 +737,46 @@
}
}
+ boolean isReduceBrightColorsActivated() {
+ try {
+ return mCdm.isReduceBrightColorsActivated();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean setReduceBrightColorsActivated(boolean activated) {
+ try {
+ return mCdm.setReduceBrightColorsActivated(activated);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ int getReduceBrightColorsStrength() {
+ try {
+ return mCdm.getReduceBrightColorsStrength();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean setReduceBrightColorsStrength(int strength) {
+ try {
+ return mCdm.setReduceBrightColorsStrength(strength);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ float getReduceBrightColorsOffsetFactor() {
+ try {
+ return mCdm.getReduceBrightColorsOffsetFactor();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
int getColorMode() {
try {
return mCdm.getColorMode();
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 95f1d12..48b05b7 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -47,20 +47,22 @@
* begins adjusting the power state to match what was requested.
* </p>
*
+ * @param groupId The identifier for the display group being requested to change power state
* @param request The requested power state.
- * @param waitForNegativeProximity If true, issues a request to wait for
+ * @param waitForNegativeProximity If {@code true}, issues a request to wait for
* negative proximity before turning the screen back on, assuming the screen
* was turned off by the proximity sensor.
- * @return True if display is ready, false if there are important changes that must
- * be made asynchronously (such as turning the screen on), in which case the caller
- * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
- * then try the request again later until the state converges.
+ * @return {@code true} if display group is ready, {@code false} if there are important
+ * changes that must be made asynchronously (such as turning the screen on), in which case
+ * the caller should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged}
+ * then try the request again later until the state converges. If the provided {@code groupId}
+ * cannot be found then {@code true} will be returned.
*/
- public abstract boolean requestPowerState(DisplayPowerRequest request,
+ public abstract boolean requestPowerState(int groupId, DisplayPowerRequest request,
boolean waitForNegativeProximity);
/**
- * Returns true if the proximity sensor screen-off function is available.
+ * Returns {@code true} if the proximity sensor screen-off function is available.
*/
public abstract boolean isProximitySensorAvailable();
@@ -71,6 +73,22 @@
public abstract int getDisplayGroupId(int displayId);
/**
+ * Registers a display group listener which will be informed of the addition, removal, or change
+ * of display groups.
+ *
+ * @param listener The listener to register.
+ */
+ public abstract void registerDisplayGroupListener(DisplayGroupListener listener);
+
+ /**
+ * Unregisters a display group listener which will be informed of the addition, removal, or
+ * change of display groups.
+ *
+ * @param listener The listener to unregister.
+ */
+ public abstract void unregisterDisplayGroupListener(DisplayGroupListener listener);
+
+ /**
* Screenshot for internal system-only use such as rotation, etc. This method includes
* secure layers and the result should never be exposed to non-system applications.
* This method does not apply any rotation and provides the output in natural orientation.
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
index 7b1033d..ef4f5f9 100644
--- a/core/java/android/hardware/display/IColorDisplayManager.aidl
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -45,4 +45,10 @@
boolean isDisplayWhiteBalanceEnabled();
boolean setDisplayWhiteBalanceEnabled(boolean enabled);
+
+ boolean isReduceBrightColorsActivated();
+ boolean setReduceBrightColorsActivated(boolean activated);
+ int getReduceBrightColorsStrength();
+ boolean setReduceBrightColorsStrength(int strength);
+ float getReduceBrightColorsOffsetFactor();
}
\ No newline at end of file
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 2650dc5..f097651 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -19,6 +19,8 @@
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
+import android.annotation.NonNull;
+import android.content.Context;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.Parcel;
@@ -81,10 +83,36 @@
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken) {
- // TODO: Value should be provided from the HAL
+ // TODO(b/179175438): Value should be provided from the HAL
this(sensorId, strength, maxEnrollmentsPerUser, sensorType,
- resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */,
- 1769 /* sensorLocationY */, 130 /* sensorRadius */);
+ resetLockoutRequiresHardwareAuthToken, 0 /* sensorLocationX */,
+ 0 /* sensorLocationY */, 0 /* sensorRadius */);
+ }
+
+ /**
+ * Initializes SensorProperties with specified values and values obtained from resources using
+ * context.
+ */
+ // TODO(b/179175438): Remove this constructor once all HALs move to AIDL.
+ public FingerprintSensorPropertiesInternal(@NonNull Context context, int sensorId,
+ @SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
+ @FingerprintSensorProperties.SensorType int sensorType,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ super(sensorId, strength, maxEnrollmentsPerUser);
+ this.sensorType = sensorType;
+ this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+
+ int[] props = context.getResources().getIntArray(
+ com.android.internal.R.array.config_udfps_sensor_props);
+ if (props != null && props.length == 3) {
+ this.sensorLocationX = props[0];
+ this.sensorLocationY = props[1];
+ this.sensorRadius = props[2];
+ } else {
+ this.sensorLocationX = 0;
+ this.sensorLocationY = 0;
+ this.sensorRadius = 0;
+ }
}
protected FingerprintSensorPropertiesInternal(Parcel in) {
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index b39df4d..c69c47f 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -93,6 +93,10 @@
int[] getVibratorIds(int deviceId);
boolean isVibrating(int deviceId);
+ // Input device battery query.
+ int getBatteryStatus(int deviceId);
+ int getBatteryCapacity(int deviceId);
+
void setPointerIconType(int typeId);
void setCustomPointerIcon(in PointerIcon icon);
diff --git a/core/java/android/hardware/input/InputDeviceBattery.java b/core/java/android/hardware/input/InputDeviceBattery.java
new file mode 100644
index 0000000..0fe124e
--- /dev/null
+++ b/core/java/android/hardware/input/InputDeviceBattery.java
@@ -0,0 +1,65 @@
+/*
+ * 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.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.IInputConstants.INVALID_BATTERY_CAPACITY;
+
+import android.hardware.Battery;
+
+/**
+ * Battery implementation for input devices.
+ *
+ * @hide
+ */
+public final class InputDeviceBattery extends Battery {
+ private static final float NULL_BATTERY_CAPACITY = -1.0f;
+
+ private final InputManager mInputManager;
+ private final int mDeviceId;
+ private final boolean mHasBattery;
+
+ InputDeviceBattery(InputManager inputManager, int deviceId, boolean hasBattery) {
+ mInputManager = inputManager;
+ mDeviceId = deviceId;
+ mHasBattery = hasBattery;
+ }
+
+ @Override
+ public boolean hasBattery() {
+ return mHasBattery;
+ }
+
+ @Override
+ public int getStatus() {
+ if (!mHasBattery) {
+ return BATTERY_STATUS_UNKNOWN;
+ }
+ return mInputManager.getBatteryStatus(mDeviceId);
+ }
+
+ @Override
+ public float getCapacity() {
+ if (mHasBattery) {
+ int capacity = mInputManager.getBatteryCapacity(mDeviceId);
+ if (capacity != INVALID_BATTERY_CAPACITY) {
+ return (float) capacity / 100.0f;
+ }
+ }
+ return NULL_BATTERY_CAPACITY;
+ }
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6ab1106..185c59d 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -25,6 +25,7 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -909,18 +910,17 @@
}
/**
- * Returns the maximum allowed obscuring opacity by UID to propagate touches.
+ * Returns the maximum allowed obscuring opacity per UID to propagate touches.
*
- * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
- * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
- * above the touch-consuming window.
+ * <p>For certain window types (eg. {@link LayoutParams#TYPE_APPLICATION_OVERLAY}), the decision
+ * of honoring {@link LayoutParams#FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring
+ * opacity of the windows above the touch-consuming window, per UID. Check documentation of
+ * {@link LayoutParams#FLAG_NOT_TOUCHABLE} for more details.
*
- * @see #setMaximumObscuringOpacityForTouch(Context, float)
- *
- * @hide
+ * @see LayoutParams#FLAG_NOT_TOUCHABLE
*/
- @TestApi
- public float getMaximumObscuringOpacityForTouch(@NonNull Context context) {
+ public float getMaximumObscuringOpacityForTouch() {
+ Context context = ActivityThread.currentApplication();
return Settings.Global.getFloat(context.getContentResolver(),
Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
@@ -946,17 +946,18 @@
*
* This value should be between 0 (inclusive) and 1 (inclusive).
*
- * @see #getMaximumObscuringOpacityForTouch(Context)
+ * @see #getMaximumObscuringOpacityForTouch()
*
* @hide
*/
@TestApi
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
- public void setMaximumObscuringOpacityForTouch(@NonNull Context context, float opacity) {
+ public void setMaximumObscuringOpacityForTouch(float opacity) {
if (opacity < 0 || opacity > 1) {
throw new IllegalArgumentException(
"Maximum obscuring opacity for touch should be >= 0 and <= 1");
}
+ Context context = ActivityThread.currentApplication();
Settings.Global.putFloat(context.getContentResolver(),
Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
}
@@ -1244,6 +1245,32 @@
}
/**
+ * Get the battery status of the input device
+ * @param deviceId The input device ID
+ * @hide
+ */
+ public int getBatteryStatus(int deviceId) {
+ try {
+ return mIm.getBatteryStatus(deviceId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the remaining battery capacity of the input device
+ * @param deviceId The input device ID
+ * @hide
+ */
+ public int getBatteryCapacity(int deviceId) {
+ try {
+ return mIm.getBatteryCapacity(deviceId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Add a runtime association between the input port and the display port. This overrides any
* static associations.
* @param inputPort The port of the input device.
@@ -1470,6 +1497,15 @@
}
/**
+ * Gets a battery object associated with an input device, assuming it has one.
+ * @return The battery, never null.
+ * @hide
+ */
+ public InputDeviceBattery getInputDeviceBattery(int deviceId, boolean hasBattery) {
+ return new InputDeviceBattery(this, deviceId, hasBattery);
+ }
+
+ /**
* Listens for changes in input devices.
*/
public interface InputDeviceListener {
diff --git a/core/java/android/hardware/location/ContextHubClientCallback.java b/core/java/android/hardware/location/ContextHubClientCallback.java
index 20fa753..b31b85f 100644
--- a/core/java/android/hardware/location/ContextHubClientCallback.java
+++ b/core/java/android/hardware/location/ContextHubClientCallback.java
@@ -15,6 +15,7 @@
*/
package android.hardware.location;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import java.util.concurrent.Executor;
@@ -101,4 +102,34 @@
* @param nanoAppId the ID of the nanoapp that had been disabled
*/
public void onNanoAppDisabled(ContextHubClient client, long nanoAppId) {}
+
+ /**
+ * Callback invoked when a {@link ContextHubClient}'s authorization to communicate with a
+ * nanoapp changes. This typically happens as a result of the app that created the
+ * {@link ContextHubClient} gaining or losing the permissions required to communicate with a
+ * nanoapp.
+ *
+ * An example of the connection callbacks looks like:
+ * 1) {@link ContextHubClient} sends message to nanoapp and holds required permissions
+ * 2) {@link ContextHubClient} loses required permissions
+ * 3) Callback invoked with the nanoapp ID and
+ * {@link ContextHubManager#AUTHORIZATION_DENIED_GRACE_PERIOD}
+ * 4) {@link ContextHubClient} performs any cleanup required with the nanoapp
+ * 5) Callback invoked with the nanoapp ID and {@link ContextHubManager#AUTHORIZATION_DENIED}.
+ * At this point, any further attempts of communication between the nanoapp and the
+ * {@link ContextHubClient} will be dropped by the contexthub along with
+ * {@link ContextHubManager#AUTHORIZATION_DENIED} being sent. The {@link ContextHubClient}
+ * should assume no communciation can happen again until
+ * {@link ContextHubManager#AUTHORIZATION_GRANTED} is received.
+ *
+ * @param client the client that is associated with this callback
+ * @param nanoAppId the ID of the nanoapp associated with the new
+ * authorization state
+ * @param authorization the authorization state denoting the ability of the
+ * client to communicate with the nanoapp
+ */
+ public void onClientAuthorizationChanged(
+ @NonNull ContextHubClient client,
+ long nanoAppId,
+ @ContextHubManager.AuthorizationState int authorization) {}
}
diff --git a/core/java/android/hardware/location/ContextHubIntentEvent.java b/core/java/android/hardware/location/ContextHubIntentEvent.java
index 917f62b..3e8f421 100644
--- a/core/java/android/hardware/location/ContextHubIntentEvent.java
+++ b/core/java/android/hardware/location/ContextHubIntentEvent.java
@@ -43,39 +43,45 @@
private final int mNanoAppAbortCode;
+ private final int mClientAuthorizationState;
+
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
- long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode) {
+ long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode,
+ @ContextHubManager.AuthorizationState int clientAuthorizationState) {
mContextHubInfo = contextHubInfo;
mEventType = eventType;
mNanoAppId = nanoAppId;
mNanoAppMessage = nanoAppMessage;
mNanoAppAbortCode = nanoAppAbortCode;
+ mClientAuthorizationState = clientAuthorizationState;
}
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType) {
this(contextHubInfo, eventType, -1 /* nanoAppId */, null /* nanoAppMessage */,
- -1 /* nanoAppAbortCode */);
+ -1 /* nanoAppAbortCode */, 0 /* clientAuthorizationState */);
}
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
long nanoAppId) {
this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */,
- -1 /* nanoAppAbortCode */);
+ -1 /* nanoAppAbortCode */, 0 /* clientAuthorizationState */);
}
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
long nanoAppId, @NonNull NanoAppMessage nanoAppMessage) {
- this(contextHubInfo, eventType, nanoAppId, nanoAppMessage, -1 /* nanoAppAbortCode */);
+ this(contextHubInfo, eventType, nanoAppId, nanoAppMessage, -1 /* nanoAppAbortCode */,
+ 0 /* clientAuthorizationState */);
}
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
long nanoAppId, int nanoAppAbortCode) {
- this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, nanoAppAbortCode);
+ this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, nanoAppAbortCode,
+ 0 /* clientAuthorizationState */);
}
/**
@@ -105,7 +111,8 @@
case ContextHubManager.EVENT_NANOAPP_ENABLED:
case ContextHubManager.EVENT_NANOAPP_DISABLED:
case ContextHubManager.EVENT_NANOAPP_ABORTED:
- case ContextHubManager.EVENT_NANOAPP_MESSAGE: // fall through
+ case ContextHubManager.EVENT_NANOAPP_MESSAGE:
+ case ContextHubManager.EVENT_CLIENT_AUTHORIZATION: // fall through
long nanoAppId = getLongExtraOrThrow(intent, ContextHubManager.EXTRA_NANOAPP_ID);
if (eventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
hasExtraOrThrow(intent, ContextHubManager.EXTRA_MESSAGE);
@@ -120,6 +127,11 @@
int nanoAppAbortCode = getIntExtraOrThrow(
intent, ContextHubManager.EXTRA_NANOAPP_ABORT_CODE);
event = new ContextHubIntentEvent(info, eventType, nanoAppId, nanoAppAbortCode);
+ } else if (eventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
+ int authState = getIntExtraOrThrow(
+ intent, ContextHubManager.EXTRA_CLIENT_AUTHORIZATION_STATE);
+ event = new ContextHubIntentEvent(info, eventType, nanoAppId,
+ null /* nanoAppMessage */, -1 /* nanoAppAbortCode */, authState);
} else {
event = new ContextHubIntentEvent(info, eventType, nanoAppId);
}
@@ -192,6 +204,21 @@
return mNanoAppMessage;
}
+ /**
+ * @return the client authorization state
+ *
+ * @throws UnsupportedOperationException if this was not a client authorization state event
+ */
+ @ContextHubManager.AuthorizationState
+ public int getClientAuthorizationState() {
+ if (mEventType != ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
+ throw new UnsupportedOperationException(
+ "Cannot invoke getClientAuthorizationState() on non-authorization event: "
+ + mEventType);
+ }
+ return mClientAuthorizationState;
+ }
+
@NonNull
@Override
public String toString() {
@@ -207,6 +234,9 @@
if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
out += ", nanoAppMessage = " + mNanoAppMessage;
}
+ if (mEventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
+ out += ", clientAuthState = " + mClientAuthorizationState;
+ }
return out + "]";
}
@@ -233,6 +263,9 @@
if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
isEqual &= other.getNanoAppMessage().equals(mNanoAppMessage);
}
+ if (mEventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
+ isEqual &= other.getClientAuthorizationState() == mClientAuthorizationState;
+ }
} catch (UnsupportedOperationException e) {
isEqual = false;
}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index d444807..ebb3021 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -59,6 +59,12 @@
private static final String TAG = "ContextHubManager";
/**
+ * An extra of type int describing the client's authorization state.
+ */
+ public static final String EXTRA_CLIENT_AUTHORIZATION_STATE =
+ "android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE";
+
+ /**
* An extra of type {@link ContextHubInfo} describing the source of the event.
*/
public static final String EXTRA_CONTEXT_HUB_INFO =
@@ -86,6 +92,42 @@
public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
/**
+ * Constants describing if a {@link ContextHubClient} and a {@link NanoApp} are authorized to
+ * communicate.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "AUTHORIZATION_" }, value = {
+ AUTHORIZATION_DENIED,
+ AUTHORIZATION_DENIED_GRACE_PERIOD,
+ AUTHORIZATION_GRANTED,
+ })
+ public @interface AuthorizationState { }
+
+ /**
+ * Indicates that the {@link ContextHubClient} can no longer communicate with a nanoapp. If the
+ * {@link ContextHubClient} attempts to send messages to the nanoapp, it will continue to
+ * receive this authorization state if the connection is still closed.
+ */
+ public static final int AUTHORIZATION_DENIED = 0;
+
+ /**
+ * Indicates the {@link ContextHubClient} will soon lose its authorization to communicate with a
+ * nanoapp. The {@link ContextHubClient} must perform any cleanup with the nanoapp as soon as
+ * possible.
+ *
+ * Note that the time between this state event and {@link AUTHORIZATION_DENIED} must be enough
+ * for the {@link ContextHubClient} to send at least one message to the nanoapp.
+ */
+ public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1;
+
+ /**
+ * The {@link ContextHubClient} is authorized to communicate with the nanoapp.
+ */
+ public static final int AUTHORIZATION_GRANTED = 2;
+
+ /**
* Constants describing the type of events from a Context Hub.
* {@hide}
*/
@@ -98,6 +140,7 @@
EVENT_NANOAPP_ABORTED,
EVENT_NANOAPP_MESSAGE,
EVENT_HUB_RESET,
+ EVENT_CLIENT_AUTHORIZATION,
})
public @interface Event { }
@@ -138,6 +181,14 @@
*/
public static final int EVENT_HUB_RESET = 6;
+ /**
+ * An event describing a client authorization state change. See
+ * {@link ContextHubClientCallback#onClientAuthorizationChanged} for more details on when this
+ * event will be sent. Contains the EXTRA_NANOAPP_ID and EXTRA_CLIENT_AUTHORIZATION_STATE
+ * extras.
+ */
+ public static final int EVENT_CLIENT_AUTHORIZATION = 7;
+
private final Looper mMainLooper;
private final IContextHubService mService;
private Callback mCallback;
@@ -747,6 +798,14 @@
public void onNanoAppDisabled(long nanoAppId) {
executor.execute(() -> callback.onNanoAppDisabled(client, nanoAppId));
}
+
+ @Override
+ public void onClientAuthorizationChanged(
+ long nanoAppId, @ContextHubManager.AuthorizationState int authorization) {
+ executor.execute(
+ () -> callback.onClientAuthorizationChanged(
+ client, nanoAppId, authorization));
+ }
};
}
@@ -757,9 +816,10 @@
* registration succeeds, the client can send messages to nanoapps through the returned
* {@link ContextHubClient} object, and receive notifications through the provided callback.
*
+ * @param context the context of the application
* @param hubInfo the hub to attach this client to
- * @param callback the notification callback to register
* @param executor the executor to invoke the callback
+ * @param callback the notification callback to register
* @return the registered client object
*
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
@@ -773,8 +833,9 @@
android.Manifest.permission.ACCESS_CONTEXT_HUB
})
@NonNull public ContextHubClient createClient(
- @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
- @NonNull @CallbackExecutor Executor executor) {
+ @Nullable Context context, @NonNull ContextHubInfo hubInfo,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull ContextHubClientCallback callback) {
Objects.requireNonNull(callback, "Callback cannot be null");
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
Objects.requireNonNull(executor, "Executor cannot be null");
@@ -783,9 +844,14 @@
IContextHubClientCallback clientInterface = createClientCallback(
client, callback, executor);
+ String attributionTag = null;
+ if (context != null) {
+ attributionTag = context.getAttributionTag();
+ }
+
IContextHubClient clientProxy;
try {
- clientProxy = mService.createClient(hubInfo.getId(), clientInterface);
+ clientProxy = mService.createClient(hubInfo.getId(), clientInterface, attributionTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -794,19 +860,25 @@
return client;
}
+
+ /**
+ * Equivalent to
+ * {@link #createClient(ContextHubInfo, Executor, String, ContextHubClientCallback)}
+ * with the {@link Context} being set to null.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
+ @NonNull public ContextHubClient createClient(
+ @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
+ @NonNull @CallbackExecutor Executor executor) {
+ return createClient(null /* context */, hubInfo, executor, callback);
+ }
+
/**
* Equivalent to {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
* with the executor using the main thread's Looper.
- *
- * @param hubInfo the hub to attach this client to
- * @param callback the notification callback to register
- * @return the registered client object
- *
- * @throws IllegalArgumentException if hubInfo does not represent a valid hub
- * @throws IllegalStateException if there were too many registered clients at the service
- * @throws NullPointerException if callback or hubInfo is null
- *
- * @see ContextHubClientCallback
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.LOCATION_HARDWARE,
@@ -814,7 +886,8 @@
})
@NonNull public ContextHubClient createClient(
@NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) {
- return createClient(hubInfo, callback, new HandlerExecutor(Handler.getMain()));
+ return createClient(null /* context */, hubInfo, new HandlerExecutor(Handler.getMain()),
+ callback);
}
/**
@@ -848,6 +921,8 @@
* on the provided PendingIntent, then the client will be automatically unregistered by the
* service.
*
+ * @param context the context of the application. If a PendingIntent client is recreated,
+ * the latest state in the context will be used and old state will be discarded
* @param hubInfo the hub to attach this client to
* @param pendingIntent the PendingIntent to register to the client
* @param nanoAppId the ID of the nanoapp that Intent events will be generated for
@@ -862,16 +937,22 @@
android.Manifest.permission.ACCESS_CONTEXT_HUB
})
@NonNull public ContextHubClient createClient(
- @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
+ @Nullable Context context, @NonNull ContextHubInfo hubInfo,
+ @NonNull PendingIntent pendingIntent, long nanoAppId) {
Objects.requireNonNull(pendingIntent);
Objects.requireNonNull(hubInfo);
ContextHubClient client = new ContextHubClient(hubInfo, true /* persistent */);
+ String attributionTag = null;
+ if (context != null) {
+ attributionTag = context.getAttributionTag();
+ }
+
IContextHubClient clientProxy;
try {
clientProxy = mService.createPendingIntentClient(
- hubInfo.getId(), pendingIntent, nanoAppId);
+ hubInfo.getId(), pendingIntent, nanoAppId, attributionTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -881,6 +962,19 @@
}
/**
+ * Equivalent to {@link #createClient(ContextHubInfo, PendingIntent, long, String)}
+ * with {@link Context} being set to null.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
+ @NonNull public ContextHubClient createClient(
+ @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
+ return createClient(null /* context */, hubInfo, pendingIntent, nanoAppId);
+ }
+
+ /**
* Unregister a callback for receive messages from the context hub.
*
* @see Callback
diff --git a/core/java/android/hardware/location/IContextHubClientCallback.aidl b/core/java/android/hardware/location/IContextHubClientCallback.aidl
index 1c76bcb..bcd6b08 100644
--- a/core/java/android/hardware/location/IContextHubClientCallback.aidl
+++ b/core/java/android/hardware/location/IContextHubClientCallback.aidl
@@ -46,4 +46,7 @@
// Callback invoked when a nanoapp is disabled at the attached Context Hub.
void onNanoAppDisabled(long nanoAppId);
+
+ // Callback invoked when the authorization state of a client changes.
+ void onClientAuthorizationChanged(long nanoAppId, int authorization);
}
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 04cc563..4961195 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -59,11 +59,13 @@
int sendMessage(int contextHubHandle, int nanoAppHandle, in ContextHubMessage msg);
// Creates a client to send and receive messages
- IContextHubClient createClient(int contextHubId, in IContextHubClientCallback client);
+ IContextHubClient createClient(
+ int contextHubId, in IContextHubClientCallback client, in String attributionTag);
// Creates a PendingIntent-based client to send and receive messages
IContextHubClient createPendingIntentClient(
- int contextHubId, in PendingIntent pendingIntent, long nanoAppId);
+ int contextHubId, in PendingIntent pendingIntent, long nanoAppId,
+ in String attributionTag);
// Returns a list of ContextHub objects of available hubs
List<ContextHubInfo> getContextHubs();
diff --git a/core/java/android/hardware/location/NanoAppState.java b/core/java/android/hardware/location/NanoAppState.java
index 8de7ecd..96b1f19 100644
--- a/core/java/android/hardware/location/NanoAppState.java
+++ b/core/java/android/hardware/location/NanoAppState.java
@@ -15,10 +15,14 @@
*/
package android.hardware.location;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A class describing the nanoapp state information resulting from a query to a Context Hub.
*
@@ -29,11 +33,21 @@
private long mNanoAppId;
private int mNanoAppVersion;
private boolean mIsEnabled;
+ private List<String> mNanoAppPermissions;
public NanoAppState(long nanoAppId, int appVersion, boolean enabled) {
mNanoAppId = nanoAppId;
mNanoAppVersion = appVersion;
mIsEnabled = enabled;
+ mNanoAppPermissions = new ArrayList<String>();
+ }
+
+ public NanoAppState(long nanoAppId, int appVersion, boolean enabled,
+ @NonNull List<String> nanoAppPermissions) {
+ mNanoAppId = nanoAppId;
+ mNanoAppVersion = appVersion;
+ mIsEnabled = enabled;
+ mNanoAppPermissions = nanoAppPermissions;
}
/**
@@ -57,10 +71,19 @@
return mIsEnabled;
}
+ /**
+ * @return List of Android permissions that are required to communicate with this app.
+ */
+ public @NonNull List<String> getNanoAppPermissions() {
+ return mNanoAppPermissions;
+ }
+
private NanoAppState(Parcel in) {
mNanoAppId = in.readLong();
mNanoAppVersion = in.readInt();
mIsEnabled = (in.readInt() == 1);
+ mNanoAppPermissions = new ArrayList<String>();
+ in.readStringList(mNanoAppPermissions);
}
@Override
@@ -73,6 +96,7 @@
out.writeLong(mNanoAppId);
out.writeInt(mNanoAppVersion);
out.writeInt(mIsEnabled ? 1 : 0);
+ out.writeStringList(mNanoAppPermissions);
}
public static final @android.annotation.NonNull Creator<NanoAppState> CREATOR =
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index c28bab7..5bac481 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -1198,7 +1198,10 @@
/**
* Returns whether the given functions are valid inputs to UsbManager.
- * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted.
+ * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI, NCM are accepted.
+ *
+ * Only one function may be set at a time, except for RNDIS and NCM, which can be set together
+ * because from a user perspective they are the same function (tethering).
*
* @return Whether the mask is settable.
*
@@ -1206,7 +1209,9 @@
*/
public static boolean areSettableFunctions(long functions) {
return functions == FUNCTION_NONE
- || ((~SETTABLE_FUNCTIONS & functions) == 0 && Long.bitCount(functions) == 1);
+ || ((~SETTABLE_FUNCTIONS & functions) == 0
+ && ((Long.bitCount(functions) == 1)
+ || (functions == (FUNCTION_RNDIS | FUNCTION_NCM))));
}
/**
diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/inputmethodservice/OWNERS
+++ b/core/java/android/inputmethodservice/OWNERS
@@ -2,3 +2,5 @@
set noparent
include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index d5aede7..0baf11e 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -23,7 +23,7 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
-import android.net.VpnInfo;
+import android.net.UnderlyingNetworkInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
@@ -70,7 +70,7 @@
in Network[] defaultNetworks,
in NetworkState[] networkStates,
in String activeIface,
- in VpnInfo[] vpnInfos);
+ in UnderlyingNetworkInfo[] underlyingNetworkInfos);
/** Force update of statistics. */
@UnsupportedAppUsage
void forceUpdate();
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 60923f5..70bca30 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -707,7 +707,7 @@
}
/**
- * This class represents an IpSecTunnelInterface.
+ * This class represents an IpSecTunnelInterface
*
* <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
* local endpoints for IPsec tunnels.
@@ -716,7 +716,9 @@
* applied to provide IPsec security to packets sent through the tunnel. While a tunnel
* cannot be used in standalone mode within Android, the higher layers may use the tunnel
* to create Network objects which are accessible to the Android system.
+ * @hide
*/
+ @SystemApi
public static final class IpSecTunnelInterface implements AutoCloseable {
private final String mOpPackageName;
private final IIpSecService mService;
@@ -727,26 +729,23 @@
private String mInterfaceName;
private int mResourceId = INVALID_RESOURCE_ID;
- /**
- * Get the underlying SPI held by this object.
- *
- * @hide
- */
- @SystemApi
+ /** Get the underlying SPI held by this object. */
@NonNull
public String getInterfaceName() {
return mInterfaceName;
}
/**
- * Add an address to the IpSecTunnelInterface.
+ * Add an address to the IpSecTunnelInterface
*
* <p>Add an address which may be used as the local inner address for
* tunneled traffic.
*
* @param address the local address for traffic inside the tunnel
* @param prefixLen length of the InetAddress prefix
+ * @hide
*/
+ @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -761,13 +760,15 @@
}
/**
- * Remove an address from the IpSecTunnelInterface.
+ * Remove an address from the IpSecTunnelInterface
*
- * <p>Remove an address which was previously added to the IpSecTunnelInterface.
+ * <p>Remove an address which was previously added to the IpSecTunnelInterface
*
* @param address to be removed
* @param prefixLen length of the InetAddress prefix
+ * @hide
*/
+ @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -818,7 +819,7 @@
}
/**
- * Delete an IpSecTunnelInterface.
+ * Delete an IpSecTunnelInterface
*
* <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
* resources. Any packets bound for this interface either inbound or outbound will
@@ -840,12 +841,7 @@
}
}
-
- /**
- * Check that the Interface was closed properly.
- *
- * @hide
- */
+ /** Check that the Interface was closed properly. */
@Override
protected void finalize() throws Throwable {
if (mCloseGuard != null) {
@@ -877,52 +873,17 @@
* Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
*
* <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
- * underlying network disconnects, and the {@link
- * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
+ * underlying network goes away, and the onLost() callback is received.
*
- * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
- * that go through the tunnel will need a underlying network to transit to the IPsec peer.
- * This network should almost certainly be a physical network such as WiFi.
- * @return a new {@link IpSecTunnelInterface} with the specified properties
- * @throws IOException indicating that the tunnel could not be created due to a lower-layer
- * error
- * @throws ResourceUnavailableException indicating that the number of opening tunnels has
- * reached the limit.
- */
- @NonNull
- @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
- @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
- public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull Network underlyingNetwork)
- throws ResourceUnavailableException, IOException {
-
- // TODO: Remove the need for adding two unused addresses with IPsec tunnels when {@link
- // #createIpSecTunnelInterface(localAddress, remoteAddress, underlyingNetwork)} can be
- // safely removed.
- final InetAddress address = InetAddress.getLocalHost();
- return createIpSecTunnelInterface(address, address, underlyingNetwork);
- }
-
- /**
- * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
- *
- * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
- * underlying network disconnects, and the {@link
- * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
- *
- * @param localAddress The local address of the tunnel
- * @param remoteAddress The local address of the tunnel
- * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
- * that go through the tunnel will need a underlying network to transit to the IPsec peer.
- * This network should almost certainly be a physical network such as WiFi.
- * @return a new {@link IpSecTunnelInterface} with the specified properties
- * @throws IOException indicating that the tunnel could not be created due to a lower-layer
- * error
- * @throws ResourceUnavailableException indicating that the number of opening tunnels has
- * reached the limit.
+ * @param localAddress The local addres of the tunnel
+ * @param remoteAddress The local addres of the tunnel
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
+ * This network should almost certainly be a network such as WiFi with an L2 address.
+ * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the socket could not be opened or bound
+ * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
* @hide
- * @deprecated Callers should use {@link #createIpSecTunnelInterface(Network)}
*/
- @Deprecated
@SystemApi
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@@ -946,14 +907,16 @@
* <p>Applications should probably not use this API directly.
*
*
- * @param tunnel The {@link IpSecTunnelInterface} that will use the supplied
+ * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
* transform.
- * @param direction the direction, {@link #DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+ * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
* the transform will be used.
* @param transform an {@link IpSecTransform} created in tunnel mode
- * @throws IOException indicating that the transform could not be applied due to a lower-layer
- * error
+ * @throws IOException indicating that the transform could not be applied due to a lower
+ * layer failure.
+ * @hide
*/
+ @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java
index 84985b6..9a11be0 100644
--- a/core/java/android/net/MatchAllNetworkSpecifier.java
+++ b/core/java/android/net/MatchAllNetworkSpecifier.java
@@ -32,17 +32,6 @@
*/
@SystemApi
public final class MatchAllNetworkSpecifier extends NetworkSpecifier implements Parcelable {
- /**
- * Utility method which verifies that the ns argument is not a MatchAllNetworkSpecifier and
- * throws an IllegalArgumentException if it is.
- * @hide
- */
- public static void checkNotMatchAllNetworkSpecifier(NetworkSpecifier ns) {
- if (ns instanceof MatchAllNetworkSpecifier) {
- throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
- }
- }
-
/** @hide */
@Override
public boolean canBeSatisfiedBy(NetworkSpecifier other) {
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 06d0fc1..5d8122b 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -22,11 +22,12 @@
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.os.Build;
import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.NetworkIdentityUtils;
+
import java.util.Objects;
/**
@@ -90,7 +91,8 @@
builder.append(mSubType);
}
if (mSubscriberId != null) {
- builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
+ builder.append(", subscriberId=")
+ .append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
@@ -111,7 +113,8 @@
// Not dumping mSubType, subtypes are no longer supported.
if (mSubscriberId != null) {
- proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
+ proto.write(NetworkIdentityProto.SUBSCRIBER_ID,
+ NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
@@ -150,32 +153,6 @@
}
/**
- * Scrub given IMSI on production builds.
- */
- public static String scrubSubscriberId(String subscriberId) {
- if (Build.IS_ENG) {
- return subscriberId;
- } else if (subscriberId != null) {
- // TODO: parse this as MCC+MNC instead of hard-coding
- return subscriberId.substring(0, Math.min(6, subscriberId.length())) + "...";
- } else {
- return "null";
- }
- }
-
- /**
- * Scrub given IMSI on production builds.
- */
- public static String[] scrubSubscriberId(String[] subscriberId) {
- if (subscriberId == null) return null;
- final String[] res = new String[subscriberId.length];
- for (int i = 0; i < res.length; i++) {
- res[i] = NetworkIdentity.scrubSubscriberId(subscriberId[i]);
- }
- return res;
- }
-
- /**
* Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType},
* assuming that any mobile networks are using the current IMSI. The subType if applicable,
* should be set as one of the TelephonyManager.NETWORK_TYPE_* constants, or
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 713b688..e1ef8b5 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -30,7 +31,8 @@
public class NetworkState implements Parcelable {
private static final boolean VALIDATE_ROAMING_STATE = false;
- public static final NetworkState EMPTY = new NetworkState(null, null, null, null, null, null);
+ // TODO: remove and make members @NonNull.
+ public static final NetworkState EMPTY = new NetworkState();
public final NetworkInfo networkInfo;
public final LinkProperties linkProperties;
@@ -40,9 +42,18 @@
public final String subscriberId;
public final String networkId;
- public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities, Network network, String subscriberId,
- String networkId) {
+ private NetworkState() {
+ networkInfo = null;
+ linkProperties = null;
+ networkCapabilities = null;
+ network = null;
+ subscriberId = null;
+ networkId = null;
+ }
+
+ public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+ String subscriberId, String networkId) {
this.networkInfo = networkInfo;
this.linkProperties = linkProperties;
this.networkCapabilities = networkCapabilities;
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index dc33cc7..aa61e03 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -48,6 +48,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -296,11 +297,11 @@
builder.append("matchRule=").append(getMatchRuleName(mMatchRule));
if (mSubscriberId != null) {
builder.append(", subscriberId=").append(
- NetworkIdentity.scrubSubscriberId(mSubscriberId));
+ NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mMatchSubscriberIds != null) {
builder.append(", matchSubscriberIds=").append(
- Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds)));
+ Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
diff --git a/core/java/android/net/VpnInfo.aidl b/core/java/android/net/UnderlyingNetworkInfo.aidl
similarity index 94%
rename from core/java/android/net/VpnInfo.aidl
rename to core/java/android/net/UnderlyingNetworkInfo.aidl
index 8bcaa81..a56f2f4 100644
--- a/core/java/android/net/VpnInfo.aidl
+++ b/core/java/android/net/UnderlyingNetworkInfo.aidl
@@ -16,4 +16,4 @@
package android.net;
-parcelable VpnInfo;
+parcelable UnderlyingNetworkInfo;
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java
new file mode 100644
index 0000000..7bf9231
--- /dev/null
+++ b/core/java/android/net/UnderlyingNetworkInfo.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A lightweight container used to carry information on the networks that underly a given
+ * virtual network.
+ *
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+public final class UnderlyingNetworkInfo implements Parcelable {
+ /** The owner of this network. */
+ public final int ownerUid;
+ /** The interface name of this network. */
+ @NonNull
+ public final String iface;
+ /** The names of the interfaces underlying this network. */
+ @NonNull
+ public final List<String> underlyingIfaces;
+
+ public UnderlyingNetworkInfo(int ownerUid, @NonNull String iface,
+ @NonNull List<String> underlyingIfaces) {
+ Objects.requireNonNull(iface);
+ Objects.requireNonNull(underlyingIfaces);
+ this.ownerUid = ownerUid;
+ this.iface = iface;
+ this.underlyingIfaces = Collections.unmodifiableList(new ArrayList<>(underlyingIfaces));
+ }
+
+ private UnderlyingNetworkInfo(@NonNull Parcel in) {
+ this.ownerUid = in.readInt();
+ this.iface = in.readString();
+ this.underlyingIfaces = new ArrayList<>();
+ in.readList(this.underlyingIfaces, null /*classLoader*/);
+ }
+
+ @Override
+ public String toString() {
+ return "UnderlyingNetworkInfo{"
+ + "ownerUid=" + ownerUid
+ + ", iface='" + iface + '\''
+ + ", underlyingIfaces='" + underlyingIfaces.toString() + '\''
+ + '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(ownerUid);
+ dest.writeString(iface);
+ dest.writeList(underlyingIfaces);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<UnderlyingNetworkInfo> CREATOR =
+ new Parcelable.Creator<UnderlyingNetworkInfo>() {
+ @NonNull
+ @Override
+ public UnderlyingNetworkInfo createFromParcel(@NonNull Parcel in) {
+ return new UnderlyingNetworkInfo(in);
+ }
+
+ @NonNull
+ @Override
+ public UnderlyingNetworkInfo[] newArray(int size) {
+ return new UnderlyingNetworkInfo[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof UnderlyingNetworkInfo)) return false;
+ final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o;
+ return ownerUid == that.ownerUid
+ && Objects.equals(iface, that.iface)
+ && Objects.equals(underlyingIfaces, that.underlyingIfaces);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ownerUid, iface, underlyingIfaces);
+ }
+}
diff --git a/core/java/android/net/VpnInfo.java b/core/java/android/net/VpnInfo.java
deleted file mode 100644
index cf58c57..0000000
--- a/core/java/android/net/VpnInfo.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-
-/**
- * A lightweight container used to carry information of the ongoing VPN.
- * Internal use only.
- *
- * @hide
- */
-public class VpnInfo implements Parcelable {
- public final int ownerUid;
- @Nullable
- public final String vpnIface;
- @Nullable
- public final String[] underlyingIfaces;
-
- public VpnInfo(int ownerUid, @Nullable String vpnIface, @Nullable String[] underlyingIfaces) {
- this.ownerUid = ownerUid;
- this.vpnIface = vpnIface;
- this.underlyingIfaces = underlyingIfaces;
- }
-
- private VpnInfo(@NonNull Parcel in) {
- this.ownerUid = in.readInt();
- this.vpnIface = in.readString();
- this.underlyingIfaces = in.createStringArray();
- }
-
- @Override
- public String toString() {
- return "VpnInfo{"
- + "ownerUid=" + ownerUid
- + ", vpnIface='" + vpnIface + '\''
- + ", underlyingIfaces='" + Arrays.toString(underlyingIfaces) + '\''
- + '}';
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(ownerUid);
- dest.writeString(vpnIface);
- dest.writeStringArray(underlyingIfaces);
- }
-
- @NonNull
- public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
- @NonNull
- @Override
- public VpnInfo createFromParcel(@NonNull Parcel in) {
- return new VpnInfo(in);
- }
-
- @NonNull
- @Override
- public VpnInfo[] newArray(int size) {
- return new VpnInfo[size];
- }
- };
-}
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index 250cff2..a22d41a 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -26,7 +26,7 @@
import android.widget.TextView;
import com.android.internal.util.HexDump;
-import com.android.org.bouncycastle.asn1.x509.X509Name;
+import com.android.internal.org.bouncycastle.asn1.x509.X509Name;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl
index 80ac64b..4f293ee 100644
--- a/core/java/android/net/vcn/IVcnManagementService.aidl
+++ b/core/java/android/net/vcn/IVcnManagementService.aidl
@@ -16,8 +16,11 @@
package android.net.vcn;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.os.ParcelUuid;
/**
@@ -29,4 +32,5 @@
void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
+ VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp);
}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 2d0a6d7..fa090f5 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -21,6 +21,8 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -62,10 +64,9 @@
* @hide
*/
@SystemService(Context.VCN_MANAGEMENT_SERVICE)
-public final class VcnManager {
+public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
- /** @hide */
@VisibleForTesting
public static final Map<
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
@@ -223,6 +224,37 @@
}
/**
+ * Queries the underlying network policy for a 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 VcnUnderlyingNetworkPolicyListener#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 for this Network.
+ * @param linkProperties the LinkProperties to be used in determining the Network policy for
+ * this Network.
+ * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+ * @return the VcnUnderlyingNetworkPolicy to be used for this Network.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties) {
+ requireNonNull(networkCapabilities, "networkCapabilities must not be null");
+ requireNonNull(linkProperties, "linkProperties must not be null");
+
+ try {
+ return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System
* Server.
*
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 71d2177..4cd62eb 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -216,16 +216,6 @@
}
/**
- * Sets the total amount of power consumed since BatteryStats reset, mAh.
- */
- @SuppressWarnings("unchecked")
- @NonNull
- public T setConsumedPower(double consumedPower) {
- mPowerComponentsBuilder.setTotalPowerConsumed(consumedPower);
- return (T) this;
- }
-
- /**
* Sets the amount of time used by the specified component, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the time component, e.g.
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b846142..cc86a60 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1556,8 +1556,7 @@
public int statSoftIrqTime;
public int statIdlTime;
- // Platform-level low power state stats
- public String statPlatformIdleState;
+ // Low power state stats
public String statSubsystemPowerState;
public HistoryStepDetails() {
@@ -1589,7 +1588,6 @@
out.writeInt(statIrqTime);
out.writeInt(statSoftIrqTime);
out.writeInt(statIdlTime);
- out.writeString(statPlatformIdleState);
out.writeString(statSubsystemPowerState);
}
@@ -1611,7 +1609,6 @@
statIrqTime = in.readInt();
statSoftIrqTime = in.readInt();
statIdlTime = in.readInt();
- statPlatformIdleState = in.readString();
statSubsystemPowerState = in.readString();
}
}
@@ -1656,6 +1653,7 @@
public byte batteryPlugType;
public short batteryTemperature;
+ // Battery voltage in millivolts (mV).
@UnsupportedAppUsage
public char batteryVoltage;
@@ -6599,9 +6597,6 @@
item.append(sb);
item.append(")");
}
- item.append(", PlatformIdleStat ");
- item.append(rec.stepDetails.statPlatformIdleState);
- item.append("\n");
item.append(", SubsystemPowerState ");
item.append(rec.stepDetails.statSubsystemPowerState);
@@ -6639,12 +6634,6 @@
item.append(',');
item.append(rec.stepDetails.statIdlTime);
item.append(',');
- if (rec.stepDetails.statPlatformIdleState != null) {
- item.append(rec.stepDetails.statPlatformIdleState);
- if (rec.stepDetails.statSubsystemPowerState != null) {
- item.append(',');
- }
- }
if (rec.stepDetails.statSubsystemPowerState != null) {
item.append(rec.stepDetails.statSubsystemPowerState);
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 0185ba4..16d041a 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -547,7 +547,8 @@
}
try {
- return transactNative(code, data, reply, flags);
+ boolean replyOwnsNative = (reply == null) ? false : reply.ownsNativeParcelObject();
+ return transactNative(code, data, reply, replyOwnsNative, flags);
} finally {
AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
@@ -572,7 +573,7 @@
* Native implementation of transact() for proxies
*/
public native boolean transactNative(int code, Parcel data, Parcel reply,
- int flags) throws RemoteException;
+ boolean replyOwnsNativeParcelObject, int flags) throws RemoteException;
/**
* See {@link IBinder#linkToDeath(DeathRecipient, int)}
*/
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
index 869a727..cb4e9cb 100644
--- a/core/java/android/os/CombinedVibrationEffect.java
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -364,8 +364,22 @@
@Override
public long getDuration() {
long maxDuration = Long.MIN_VALUE;
+ boolean hasUnknownStep = false;
for (int i = 0; i < mEffects.size(); i++) {
- maxDuration = Math.max(maxDuration, mEffects.valueAt(i).getDuration());
+ long duration = mEffects.valueAt(i).getDuration();
+ if (duration == Long.MAX_VALUE) {
+ // If any duration is repeating, this combination duration is also repeating.
+ return duration;
+ }
+ maxDuration = Math.max(maxDuration, duration);
+ // If any step is unknown, this combination duration will also be unknown, unless
+ // any step is repeating. Repeating vibrations take precedence over non-repeating
+ // ones in the service, so continue looping to check for repeating steps.
+ hasUnknownStep |= duration < 0;
+ }
+ if (hasUnknownStep) {
+ // If any step is unknown, this combination duration is also unknown.
+ return -1;
}
return maxDuration;
}
@@ -477,16 +491,25 @@
@Override
public long getDuration() {
+ boolean hasUnknownStep = false;
long durations = 0;
final int effectCount = mEffects.size();
for (int i = 0; i < effectCount; i++) {
CombinedVibrationEffect effect = mEffects.get(i);
long duration = effect.getDuration();
- if (duration < 0) {
- // If any duration is unknown, this combination duration is also unknown.
+ if (duration == Long.MAX_VALUE) {
+ // If any duration is repeating, this combination duration is also repeating.
return duration;
}
durations += duration;
+ // If any step is unknown, this combination duration will also be unknown, unless
+ // any step is repeating. Repeating vibrations take precedence over non-repeating
+ // ones in the service, so continue looping to check for repeating steps.
+ hasUnknownStep |= duration < 0;
+ }
+ if (hasUnknownStep) {
+ // If any step is unknown, this combination duration is also unknown.
+ return -1;
}
long delays = 0;
for (int i = 0; i < effectCount; i++) {
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index 804dc10..f9e2947 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.CombinedVibrationEffect;
+import android.os.IVibratorStateListener;
import android.os.VibrationAttributes;
import android.os.VibratorInfo;
@@ -24,6 +25,9 @@
interface IVibratorManagerService {
int[] getVibratorIds();
VibratorInfo getVibratorInfo(int vibratorId);
+ boolean isVibrating(int vibratorId);
+ boolean registerVibratorStateListener(int vibratorId, in IVibratorStateListener listener);
+ boolean unregisterVibratorStateListener(int vibratorId, in IVibratorStateListener listener);
boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
in CombinedVibrationEffect effect, in VibrationAttributes attributes);
void vibrate(int uid, String opPkg, in CombinedVibrationEffect effect,
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index fc65090..5f5a910 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3729,4 +3729,9 @@
public long getBlobAshmemSize() {
return nativeGetBlobAshmemSize(mNativePtr);
}
+
+ /** @hide */
+ /*package*/ boolean ownsNativeParcelObject() {
+ return mOwnsNativeParcelObject;
+ }
}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 18dca68..f681c4f 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -36,11 +36,15 @@
private final int mModeledPowerComponentOffset;
PowerComponents(@NonNull Builder builder) {
- mTotalPowerConsumed = builder.mTotalPowerConsumed;
mCustomPowerComponentCount = builder.mCustomPowerComponentCount;
mModeledPowerComponentOffset = builder.mModeledPowerComponentOffset;
mPowerComponents = builder.mPowerComponents;
mTimeComponents = builder.mTimeComponents;
+ double totalPower = 0;
+ for (int i = mPowerComponents.length - 1; i >= 0; i--) {
+ totalPower += mPowerComponents[i];
+ }
+ mTotalPowerConsumed = totalPower;
}
PowerComponents(@NonNull Parcel source) {
@@ -158,7 +162,6 @@
* Builder for PowerComponents.
*/
static final class Builder {
- private double mTotalPowerConsumed;
private final double[] mPowerComponents;
private final int mCustomPowerComponentCount;
private final long[] mTimeComponents;
@@ -181,15 +184,6 @@
}
/**
- * Sets the sum amount of power consumed since BatteryStats reset.
- */
- @NonNull
- public Builder setTotalPowerConsumed(double totalPowerConsumed) {
- mTotalPowerConsumed = totalPowerConsumed;
- return this;
- }
-
- /**
* Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the power component, e.g.
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 9a102a7..059e932 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -416,9 +416,21 @@
public static final int GO_TO_SLEEP_REASON_QUIESCENT = 10;
/**
+ * Go to sleep reason code: The last powered on display group has been removed.
* @hide
*/
- public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_QUIESCENT;
+ public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED = 11;
+
+ /**
+ * Go to sleep reason code: Every display group has been turned off.
+ * @hide
+ */
+ public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF = 12;
+
+ /**
+ * @hide
+ */
+ public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF;
/**
* @hide
@@ -435,6 +447,8 @@
case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive";
+ case GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED: return "display_group_removed";
+ case GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF: return "display_groups_turned_off";
default: return Integer.toString(sleepReason);
}
}
@@ -521,6 +535,8 @@
WAKE_REASON_WAKE_KEY,
WAKE_REASON_WAKE_MOTION,
WAKE_REASON_HDMI,
+ WAKE_REASON_DISPLAY_GROUP_ADDED,
+ WAKE_REASON_DISPLAY_GROUP_TURNED_ON,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WakeReason{}
@@ -608,6 +624,18 @@
public static final int WAKE_REASON_LID = 9;
/**
+ * Wake up reason code: Waking due to display group being added.
+ * @hide
+ */
+ public static final int WAKE_REASON_DISPLAY_GROUP_ADDED = 10;
+
+ /**
+ * Wake up reason code: Waking due to display group being powered on.
+ * @hide
+ */
+ public static final int WAKE_REASON_DISPLAY_GROUP_TURNED_ON = 11;
+
+ /**
* Convert the wake reason to a string for debugging purposes.
* @hide
*/
@@ -623,6 +651,8 @@
case WAKE_REASON_WAKE_MOTION: return "WAKE_REASON_WAKE_MOTION";
case WAKE_REASON_HDMI: return "WAKE_REASON_HDMI";
case WAKE_REASON_LID: return "WAKE_REASON_LID";
+ case WAKE_REASON_DISPLAY_GROUP_ADDED: return "WAKE_REASON_DISPLAY_GROUP_ADDED";
+ case WAKE_REASON_DISPLAY_GROUP_TURNED_ON: return "WAKE_REASON_DISPLAY_GROUP_TURNED_ON";
default: return Integer.toString(wakeReason);
}
}
@@ -1253,8 +1283,15 @@
}
}
- /**
- * Forces the device to go to sleep.
+ /**
+ * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+ * to turn off.
+ *
+ * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+ * turned on it will be turned off. If all displays are off as a result of this action the
+ * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
+ * default display group} is already off then nothing will happen.
+ *
* <p>
* Overrides all the wake locks that are held.
* This is what happens when the power key is pressed to turn off the screen.
@@ -1277,7 +1314,14 @@
}
/**
- * Forces the device to go to sleep.
+ * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+ * to turn off.
+ *
+ * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+ * turned on it will be turned off. If all displays are off as a result of this action the
+ * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
+ * default display group} is already off then nothing will happen.
+ *
* <p>
* Overrides all the wake locks that are held.
* This is what happens when the power key is pressed to turn off the screen.
@@ -1307,9 +1351,15 @@
}
/**
- * Forces the device to wake up from sleep.
+ * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+ * to turn on.
+ *
+ * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+ * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
+ * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already
+ * on then nothing will happen.
+ *
* <p>
- * If the device is currently asleep, wakes it up, otherwise does nothing.
* This is what happens when the power key is pressed to turn on the screen.
* </p><p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
@@ -1332,9 +1382,15 @@
}
/**
- * Forces the device to wake up from sleep.
+ * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+ * to turn on.
+ *
+ * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+ * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
+ * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already
+ * on then nothing will happen.
+ *
* <p>
- * If the device is currently asleep, wakes it up, otherwise does nothing.
* This is what happens when the power key is pressed to turn on the screen.
* </p><p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
@@ -1361,9 +1417,13 @@
}
/**
- * Forces the device to wake up from sleep.
+ * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
+ *
+ * <p>If the {@link android.view.Display#DEFAULT_DISPLAY default display} is turned off it will
+ * be turned on. Additionally, if the device is asleep it will be awoken. If the {@link
+ * android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen.
+ *
* <p>
- * If the device is currently asleep, wakes it up, otherwise does nothing.
* This is what happens when the power key is pressed to turn on the screen.
* </p><p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 71344f9..f853e67 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -288,6 +288,20 @@
}
/**
+ * Get service debug info.
+ * @return an array of information for each service (like listServices, but with PIDs)
+ * @hide
+ */
+ public static ServiceDebugInfo[] getServiceDebugInfo() {
+ try {
+ return getIServiceManager().getServiceDebugInfo();
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in getServiceDebugInfo", e);
+ return null;
+ }
+ }
+
+ /**
* This is only intended to be called when the process is first being brought
* up and bound by the activity manager. There is only one thread in the process
* at that time, so no locking is done.
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index b70b6b5..60acc57 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -103,6 +103,10 @@
throw new RemoteException();
}
+ public ServiceDebugInfo[] getServiceDebugInfo() throws RemoteException {
+ return mServiceManager.getServiceDebugInfo();
+ }
+
/**
* Same as mServiceManager but used by apps.
*
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 2093077..217f178 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -268,7 +268,7 @@
}
/** @hide */
- public String usageToString(int usage) {
+ public static String usageToString(int usage) {
switch (usage) {
case USAGE_UNKNOWN:
return "UNKNOWN";
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 7db5a80..3fbc284 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -38,14 +38,20 @@
* Opens or creates a storage given a target path and data loader params. Returns the storage ID.
*/
int openStorage(in @utf8InCpp String path);
- int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode,
- in IDataLoaderStatusListener statusListener,
- in StorageHealthCheckParams healthCheckParams,
- in IStorageHealthListener healthListener,
- in PerUidReadTimeouts[] perUidReadTimeouts);
+ int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode);
int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
/**
+ * Loops DataLoader through bind/create/start with params.
+ */
+ boolean startLoading(int storageId,
+ in DataLoaderParamsParcel params,
+ in IDataLoaderStatusListener statusListener,
+ in StorageHealthCheckParams healthCheckParams,
+ in IStorageHealthListener healthListener,
+ in PerUidReadTimeouts[] perUidReadTimeouts);
+
+ /**
* Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
*/
const int BIND_TEMPORARY = 0;
@@ -101,6 +107,14 @@
int isFileFullyLoaded(int storageId, in @utf8InCpp String path);
/**
+ * Checks if all files in the storage are fully loaded.
+ * 0 - fully loaded
+ * >0 - certain pages missing
+ * <0 - -errcode
+ */
+ int isFullyLoaded(int storageId);
+
+ /**
* Returns overall loading progress of all the files on a storage, progress value between [0,1].
* Returns a negative value on error.
*/
@@ -113,11 +127,6 @@
byte[] getMetadataById(int storageId, in byte[] fileId);
/**
- * Starts loading data for a storage.
- */
- boolean startLoading(int storageId);
-
- /**
* Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader.
*/
void deleteStorage(int storageId);
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 59292baa..f2fe719 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -37,7 +37,6 @@
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.InstallationFileParcel;
-import android.text.TextUtils;
import java.io.File;
import java.io.IOException;
@@ -53,6 +52,7 @@
private @NonNull final IncrementalManager mIncrementalManager;
private @NonNull final File mStageDir;
+ private @Nullable IncrementalStorage mInheritedStorage;
private @Nullable IncrementalStorage mDefaultStorage;
/**
@@ -65,6 +65,7 @@
*/
public static IncrementalFileStorages initialize(Context context,
@NonNull File stageDir,
+ @Nullable File inheritedDir,
@NonNull DataLoaderParams dataLoaderParams,
@Nullable IDataLoaderStatusListener statusListener,
@Nullable StorageHealthCheckParams healthCheckParams,
@@ -79,9 +80,8 @@
throw new IOException("Failed to obtain incrementalManager.");
}
- final IncrementalFileStorages result = new IncrementalFileStorages(stageDir,
- incrementalManager, dataLoaderParams, statusListener, healthCheckParams,
- healthListener, perUidReadTimeouts);
+ final IncrementalFileStorages result = new IncrementalFileStorages(stageDir, inheritedDir,
+ incrementalManager, dataLoaderParams);
for (InstallationFileParcel file : addedFiles) {
if (file.location == LOCATION_DATA_APP) {
try {
@@ -95,42 +95,45 @@
throw new IOException("Unknown file location: " + file.location);
}
}
-
- result.startLoading();
+ result.startLoading(dataLoaderParams, statusListener, healthCheckParams, healthListener,
+ perUidReadTimeouts);
return result;
}
private IncrementalFileStorages(@NonNull File stageDir,
+ @Nullable File inheritedDir,
@NonNull IncrementalManager incrementalManager,
- @NonNull DataLoaderParams dataLoaderParams,
- @Nullable IDataLoaderStatusListener statusListener,
- @Nullable StorageHealthCheckParams healthCheckParams,
- @Nullable IStorageHealthListener healthListener,
- @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
+ @NonNull DataLoaderParams dataLoaderParams) throws IOException {
try {
mStageDir = stageDir;
mIncrementalManager = incrementalManager;
- if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
- final String incrementalPath = dataLoaderParams.getArguments();
- if (TextUtils.isEmpty(incrementalPath)) {
- throw new IOException("Failed to create storage: incrementalPath is empty");
+ if (inheritedDir != null && IncrementalManager.isIncrementalPath(
+ inheritedDir.getAbsolutePath())) {
+ mInheritedStorage = mIncrementalManager.openStorage(
+ inheritedDir.getAbsolutePath());
+ if (mInheritedStorage != null) {
+ if (!mInheritedStorage.isFullyLoaded()) {
+ throw new IOException("Inherited storage has missing pages.");
+ }
+
+ mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
+ mInheritedStorage, IncrementalManager.CREATE_MODE_CREATE
+ | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+ if (mDefaultStorage == null) {
+ throw new IOException(
+ "Couldn't create linked incremental storage at " + stageDir);
+ }
+ return;
}
- mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
- if (mDefaultStorage == null) {
- throw new IOException(
- "Couldn't open incremental storage at " + incrementalPath);
- }
- mDefaultStorage.bind(stageDir.getAbsolutePath());
- } else {
- mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
- dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
- | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false,
- statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
- if (mDefaultStorage == null) {
- throw new IOException(
- "Couldn't create incremental storage at " + stageDir);
- }
+ }
+
+ mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
+ dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
+ | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+ if (mDefaultStorage == null) {
+ throw new IOException(
+ "Couldn't create incremental storage at " + stageDir);
}
} catch (IOException e) {
cleanUp();
@@ -149,9 +152,16 @@
/**
* Starts or re-starts loading of data.
*/
- public void startLoading() throws IOException {
- if (!mDefaultStorage.startLoading()) {
- throw new IOException("Failed to start loading data for Incremental installation.");
+ void startLoading(
+ @NonNull DataLoaderParams dataLoaderParams,
+ @Nullable IDataLoaderStatusListener statusListener,
+ @Nullable StorageHealthCheckParams healthCheckParams,
+ @Nullable IStorageHealthListener healthListener,
+ @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
+ if (!mDefaultStorage.startLoading(dataLoaderParams, statusListener, healthCheckParams,
+ healthListener, perUidReadTimeouts)) {
+ throw new IOException(
+ "Failed to start or restart loading data for Incremental installation.");
}
}
@@ -163,6 +173,21 @@
}
/**
+ * Creates a hardlink from inherited storage to default.
+ */
+ public boolean makeLink(@NonNull String relativePath, @NonNull String fromBase,
+ @NonNull String toBase) throws IOException {
+ if (mInheritedStorage == null) {
+ return false;
+ }
+ final File sourcePath = new File(fromBase, relativePath);
+ final File destPath = new File(toBase, relativePath);
+ mInheritedStorage.makeLink(sourcePath.getAbsolutePath(), mDefaultStorage,
+ destPath.getAbsolutePath());
+ return true;
+ }
+
+ /**
* Permanently disables readlogs.
*/
public void disallowReadLogs() {
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 4b93270..7e7057f 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -22,7 +22,6 @@
import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.DataLoaderParams;
-import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.IPackageLoadingProgressCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -95,32 +94,20 @@
* @param params IncrementalDataLoaderParams object to configure data loading.
* @param createMode Mode for opening an old Incremental File System mount or creating
* a new mount.
- * @param autoStartDataLoader Set true to immediately start data loader after creating storage.
* @return IncrementalStorage object corresponding to the mounted directory.
*/
@Nullable
public IncrementalStorage createStorage(@NonNull String path,
@NonNull DataLoaderParams params,
- @CreateMode int createMode,
- boolean autoStartDataLoader,
- @Nullable IDataLoaderStatusListener statusListener,
- @Nullable StorageHealthCheckParams healthCheckParams,
- @Nullable IStorageHealthListener healthListener,
- @NonNull PerUidReadTimeouts[] perUidReadTimeouts) {
+ @CreateMode int createMode) {
Objects.requireNonNull(path);
Objects.requireNonNull(params);
- Objects.requireNonNull(perUidReadTimeouts);
try {
- final int id = mService.createStorage(path, params.getData(), createMode,
- statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
+ final int id = mService.createStorage(path, params.getData(), createMode);
if (id < 0) {
return null;
}
- final IncrementalStorage storage = new IncrementalStorage(mService, id);
- if (autoStartDataLoader) {
- storage.startLoading();
- }
- return storage;
+ return new IncrementalStorage(mService, id);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -281,15 +268,18 @@
* Unbinds the target dir and deletes the corresponding storage instance.
* Deletes the package name and associated storage id from maps.
*/
- public void onPackageRemoved(@NonNull String codePath) {
+ public void onPackageRemoved(@NonNull File codeFile) {
try {
+ final String codePath = codeFile.getAbsolutePath();
final IncrementalStorage storage = openStorage(codePath);
if (storage == null) {
return;
}
mLoadingProgressCallbacks.cleanUpCallbacks(storage);
unregisterHealthListener(codePath);
- mService.deleteStorage(storage.getId());
+
+ // Parent since we bind-mount a folder one level above.
+ mService.deleteBindMount(storage.getId(), codeFile.getParent());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 5b688bb..e6ce8cd 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -18,11 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.DataLoaderParams;
+import android.content.pm.IDataLoaderStatusListener;
import android.os.RemoteException;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -323,6 +326,24 @@
}
}
+
+ /**
+ * Checks if all files in the storage are fully loaded.
+ */
+ public boolean isFullyLoaded() throws IOException {
+ try {
+ final int res = mService.isFullyLoaded(mId);
+ if (res < 0) {
+ throw new IOException(
+ "isFullyLoaded() failed at querying loading progress, errno " + -res);
+ }
+ return res == 0;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ }
+
/**
* Returns the loading progress of a storage
*
@@ -376,13 +397,21 @@
}
/**
- * Informs the data loader service associated with the current storage to start data loader
- *
- * @return True if data loader is successfully started.
+ * Iinitializes and starts the DataLoader.
+ * This makes sure all install-time parameters are applied.
+ * Does not affect persistent DataLoader params.
+ * @return True if start request was successfully queued.
*/
- public boolean startLoading() {
+ public boolean startLoading(
+ @NonNull DataLoaderParams dataLoaderParams,
+ @Nullable IDataLoaderStatusListener statusListener,
+ @Nullable StorageHealthCheckParams healthCheckParams,
+ @Nullable IStorageHealthListener healthListener,
+ @NonNull PerUidReadTimeouts[] perUidReadTimeouts) {
+ Objects.requireNonNull(perUidReadTimeouts);
try {
- return mService.startLoading(mId);
+ return mService.startLoading(mId, dataLoaderParams.getData(), statusListener,
+ healthCheckParams, healthListener, perUidReadTimeouts);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return false;
diff --git a/core/java/android/os/strictmode/IncorrectContextUseViolation.java b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
index 647db17..11d26ca 100644
--- a/core/java/android/os/strictmode/IncorrectContextUseViolation.java
+++ b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
@@ -16,19 +16,20 @@
package android.os.strictmode;
+import android.annotation.NonNull;
import android.content.Context;
/**
- * Incorrect usage of {@link Context}, such as obtaining a visual service from non-visual
- * {@link Context} instance.
+ * Incorrect usage of {@link Context}, such as obtaining a UI service from non-UI {@link Context}
+ * instance.
+ *
* @see Context#getSystemService(String)
- * @see Context#getDisplayNoVerify()
- * @hide
+ * @see Context#isUiContext(Context)
+ * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
*/
public final class IncorrectContextUseViolation extends Violation {
- /** @hide */
- public IncorrectContextUseViolation(String message, Throwable originStack) {
+ public IncorrectContextUseViolation(@NonNull String message, @NonNull Throwable originStack) {
super(message);
initCause(originStack);
}
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index 4224b7a..dfe748b 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -809,6 +809,9 @@
permissions. It is unlikely that 3rd party apps will be able to use APIs protected by signature
permissions as they are usually not signed with the platform certificate.
+If possible, [role protected permissions](#role-protected-permissions) should also be considered as
+an alternative to better restrict which apps may get the permission.
+
Such permissions are defined and checked like an install time permission.
### Preinstalled permissions
@@ -819,6 +822,9 @@
Hence this permission level is discouraged unless there are
[further restrictions](#restricted-by-tests).
+If possible, [role protected permissions](#role-protected-permissions) should also be considered as
+an alternative to better restrict which apps may get the permission.
+
Such permissions are defined and checked like an install time permission.
### Privileged permissions
@@ -833,6 +839,9 @@
Hence this permission level is discouraged unless there are
[further restrictions](#restricted-by-tests).
+If possible, [role protected permissions](#role-protected-permissions) should also be considered as
+an alternative to better restrict which apps may get the permission.
+
Such permissions are defined and checked like an install time permission.
#### Restricted by tests
@@ -890,8 +899,16 @@
Which apps qualify for such a permission level is flexible and custom for each such level. Usually
they refer to a single or small set of apps, usually - but not always - apps defined in AOSP.
+This type of permission is deprecated in favor of
+[role protected permissions](#role-protected-permissions).
+
These permissions are defined and checked like an install time permission.
+### Role protected permissions
+
+See
+[Using role for permission protection](../../../../../../packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/role/RolePermissionProtection.md).
+
### Development permissions
> Not recommended
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index a29b4b9..91e091c 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -368,6 +368,14 @@
public static final String NAMESPACE_SYSTEMUI = "systemui";
/**
+ * Namespace for system time and time zone detection related features / behavior.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_SYSTEM_TIME = "system_time";
+
+ /**
* Telephony related properties.
*
* @hide
diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS
index cb1509a..cb06515 100644
--- a/core/java/android/provider/OWNERS
+++ b/core/java/android/provider/OWNERS
@@ -1,5 +1,6 @@
per-file *BlockedNumber* = file:/telephony/OWNERS
per-file *Telephony* = file:/telephony/OWNERS
+per-file *SimPhonebook* = file:/telephony/OWNERS
per-file *CallLog* = file:platform/packages/providers/ContactsProvider:/OWNERS
per-file *Contacts* = file:platform/packages/providers/ContactsProvider:/OWNERS
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 79f055e..9603f4d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5931,6 +5931,13 @@
public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
/**
+ * Setting key to indicate whether camera-based autorotate is enabled.
+ *
+ * @hide
+ */
+ public static final String CAMERA_AUTOROTATE = "camera_autorotate";
+
+ /**
* @deprecated Use {@link android.provider.Settings.Global#DEVELOPMENT_SETTINGS_ENABLED}
* instead
*/
@@ -13393,6 +13400,24 @@
"euicc_factory_reset_timeout_millis";
/**
+ * Flag to set the waiting time for euicc slot switch.
+ * Type: long
+ *
+ * @hide
+ */
+ public static final String EUICC_SWITCH_SLOT_TIMEOUT_MILLIS =
+ "euicc_switch_slot_timeout_millis";
+
+ /**
+ * Flag to set the waiting time for enabling multi SIM slot.
+ * Type: long
+ *
+ * @hide
+ */
+ public static final String ENABLE_MULTI_SLOT_TIMEOUT_MILLIS =
+ "enable_multi_slot_timeout_millis";
+
+ /**
* Flag to set the timeout for when to refresh the storage settings cached data.
* Type: long
*
@@ -13555,6 +13580,28 @@
public static final String POWER_BUTTON_VERY_LONG_PRESS =
"power_button_very_long_press";
+
+ /**
+ * Keyguard should be on the left hand side of the screen, for wide screen layouts.
+ *
+ * @hide
+ */
+ public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0;
+
+ /**
+ * Keyguard should be on the right hand side of the screen, for wide screen layouts.
+ *
+ * @hide
+ */
+ public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1;
+ /**
+ * In one handed mode, which side the keyguard should be on. Allowable values are one of
+ * the ONE_HANDED_KEYGUARD_SIDE_* constants.
+ *
+ * @hide
+ */
+ public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
+
/**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
@@ -14721,9 +14768,8 @@
* touch, allow the UID to propagate the touch.
* </ul>
*
- * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch(Context)
- * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(Context,
- * float)
+ * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch()
+ * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(float)
*
* @hide
*/
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
new file mode 100644
index 0000000..2efc212
--- /dev/null
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -0,0 +1,506 @@
+/*
+ * 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.provider;
+
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN_PATH_SEGMENT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * 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.
+ */
+public final class SimPhonebookContract {
+
+ /** The authority for the SIM phonebook provider. */
+ public static final String AUTHORITY = "com.android.simphonebook";
+ /** The content:// style uri to the authority for the SIM phonebook provider. */
+ @NonNull
+ public static final Uri AUTHORITY_URI = Uri.parse("content://com.android.simphonebook");
+ /**
+ * The Uri path element used to indicate that the following path segment is a subscription ID
+ * for the SIM card that will be operated on.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+
+ private SimPhonebookContract() {
+ }
+
+ /**
+ * Returns the Uri path segment used to reference the specified elementary file type for Uris
+ * returned by this API.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
+ switch (efType) {
+ case EF_ADN:
+ return EF_ADN_PATH_SEGMENT;
+ case EF_FDN:
+ return EF_FDN_PATH_SEGMENT;
+ case EF_SDN:
+ return EF_SDN_PATH_SEGMENT;
+ default:
+ throw new IllegalArgumentException("Unsupported EfType " + efType);
+ }
+ }
+
+ /** Constants for the contact records on a SIM card. */
+ public static final class SimRecords {
+
+ /**
+ * The subscription ID of the SIM the record is from.
+ *
+ * @see SubscriptionInfo#getSubscriptionId()
+ */
+ public static final String SUBSCRIPTION_ID = "subscription_id";
+ /**
+ * The type of the elementary file the record is from.
+ *
+ * @see ElementaryFiles#EF_ADN
+ * @see ElementaryFiles#EF_FDN
+ * @see ElementaryFiles#EF_SDN
+ */
+ public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+ /**
+ * The 1-based offset of the record in the elementary file that contains it.
+ *
+ * <p>This can be used to access individual SIM records by appending it to the
+ * elementary file URIs but it is not like a normal database ID because it is not
+ * auto-incrementing and it is not unique across SIM cards or elementary files. Hence, care
+ * should be taken when using it to ensure that it is applied to the correct SIM and EF.
+ *
+ * @see #getItemUri(int, int, int)
+ */
+ public static final String RECORD_NUMBER = "record_number";
+ /**
+ * The name for this record.
+ *
+ * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+ * exceeds the maximum supported length or contains unsupported characters.
+ * {@link #validateName(ContentResolver, int, int, String)} )} can be used to
+ * check whether the name is supported.
+ *
+ * @see ElementaryFiles#NAME_MAX_LENGTH
+ * @see #validateName(ContentResolver, int, int, String) )
+ */
+ public static final String NAME = "name";
+ /**
+ * The phone number for this record.
+ *
+ * <p>Only dialable characters are supported.
+ *
+ * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+ * exceeds the maximum supported length or contains unsupported characters.
+ *
+ * @see ElementaryFiles#PHONE_NUMBER_MAX_LENGTH
+ * @see android.telephony.PhoneNumberUtils#isDialable(char)
+ */
+ public static final String PHONE_NUMBER = "phone_number";
+
+ /** The MIME type of a CONTENT_URI subdirectory of a single SIM record. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+ /** The MIME type of CONTENT_URI providing a directory of SIM records. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+
+ /**
+ * The path segment that is appended to {@link #getContentUri(int, int)} which indicates
+ * that the following path segment contains a name to be validated.
+ *
+ * @hide
+ * @see #validateName(ContentResolver, int, int, String)
+ */
+ @SystemApi
+ public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+
+ /**
+ * The key for a cursor extra that contains the result of a validate name query.
+ *
+ * @hide
+ * @see #validateName(ContentResolver, int, int, String)
+ */
+ @SystemApi
+ public static final String EXTRA_NAME_VALIDATION_RESULT =
+ "android.provider.extra.NAME_VALIDATION_RESULT";
+
+
+ /**
+ * Key for the PIN2 needed to modify FDN record that should be passed in the Bundle
+ * passed to {@link ContentResolver#insert(Uri, ContentValues, Bundle)},
+ * {@link ContentResolver#update(Uri, ContentValues, Bundle)}
+ * and {@link ContentResolver#delete(Uri, Bundle)}.
+ *
+ * <p>Modifying FDN records also requires either
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
+ * {@link TelephonyManager#hasCarrierPrivileges()}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+
+ private SimRecords() {
+ }
+
+ /**
+ * Returns the content Uri for the specified elementary file on the specified SIM.
+ *
+ * <p>When queried this Uri will return all of the contact records in the specified
+ * elementary file on the specified SIM. The available subscriptionIds and efTypes can
+ * 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}
+ *
+ * @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
+ * @see ElementaryFiles#EF_ADN
+ * @see ElementaryFiles#EF_FDN
+ * @see ElementaryFiles#EF_SDN
+ */
+ @NonNull
+ public static Uri getContentUri(int subscriptionId, @ElementaryFiles.EfType int efType) {
+ return buildContentUri(subscriptionId, efType).build();
+ }
+
+ /**
+ * Content Uri for the specific SIM record with the provided {@link #RECORD_NUMBER}.
+ *
+ * <p>When queried this will return the record identified by the provided arguments.
+ *
+ * <p>For a non-existent record:
+ * <ul>
+ * <li>query will return an empty cursor</li>
+ * <li>update will return 0</li>
+ * <li>delete will return 0</li>
+ * </ul>
+ *
+ * @param subscriptionId the subscription ID of the SIM containing the record. If no SIM
+ * with this subscription ID exists then it will be treated as a
+ * non-existent record
+ * @param efType the elementary file type containing the record. If the specified
+ * SIM doesn't support this elementary file then it will be treated
+ * as a non-existent record.
+ * @param recordNumber the record number of the record this Uri should reference. This
+ * 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.
+ */
+ @NonNull
+ public static Uri getItemUri(
+ int subscriptionId, @ElementaryFiles.EfType int efType, int recordNumber) {
+ // Elementary file record indices are 1-based.
+ Preconditions.checkArgument(recordNumber > 0, "Invalid recordNumber");
+
+ return buildContentUri(subscriptionId, efType)
+ .appendPath(String.valueOf(recordNumber))
+ .build();
+ }
+
+ /**
+ * Validates a value that is being provided for the {@link #NAME} column.
+ *
+ * <p>The return value can be used to check if the name is valid. If it is not valid then
+ * inserts and updates to the specified elementary file that use the provided name value
+ * will throw an {@link IllegalArgumentException}.
+ *
+ * <p>If the specified SIM or elementary file don't exist then
+ * {@link NameValidationResult#getMaxEncodedLength()} will be zero and
+ * {@link NameValidationResult#isValid()} will return false.
+ */
+ @NonNull
+ @WorkerThread
+ public static NameValidationResult validateName(
+ @NonNull ContentResolver resolver, int subscriptionId,
+ @ElementaryFiles.EfType int efType,
+ @NonNull String name) {
+ Bundle queryArgs = new Bundle();
+ queryArgs.putString(SimRecords.NAME, name);
+ try (Cursor cursor =
+ resolver.query(buildContentUri(subscriptionId, efType)
+ .appendPath(VALIDATE_NAME_PATH_SEGMENT)
+ .build(), null, queryArgs, null)) {
+ NameValidationResult result = cursor.getExtras()
+ .getParcelable(EXTRA_NAME_VALIDATION_RESULT);
+ return result != null ? result : new NameValidationResult(name, "", 0, 0);
+ }
+ }
+
+ private static Uri.Builder buildContentUri(
+ int subscriptionId, @ElementaryFiles.EfType int efType) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(AUTHORITY)
+ .appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+ .appendPath(String.valueOf(subscriptionId))
+ .appendPath(getEfUriPath(efType));
+ }
+
+ /** Contains details about the validity of a value provided for the {@link #NAME} column. */
+ public static final class NameValidationResult implements Parcelable {
+
+ @NonNull
+ public static final Creator<NameValidationResult> CREATOR =
+ new Creator<NameValidationResult>() {
+
+ @Override
+ public NameValidationResult createFromParcel(@NonNull Parcel in) {
+ return new NameValidationResult(in);
+ }
+
+ @NonNull
+ @Override
+ public NameValidationResult[] newArray(int size) {
+ return new NameValidationResult[size];
+ }
+ };
+
+ private final String mName;
+ private final String mSanitizedName;
+ private final int mEncodedLength;
+ private final int mMaxEncodedLength;
+
+ /** Creates a new instance from the provided values. */
+ public NameValidationResult(@NonNull String name, @NonNull String sanitizedName,
+ int encodedLength, int maxEncodedLength) {
+ this.mName = Objects.requireNonNull(name);
+ this.mSanitizedName = Objects.requireNonNull(sanitizedName);
+ this.mEncodedLength = encodedLength;
+ this.mMaxEncodedLength = maxEncodedLength;
+ }
+
+ private NameValidationResult(Parcel in) {
+ this(in.readString(), in.readString(), in.readInt(), in.readInt());
+ }
+
+ /** Returns the original name that is being validated. */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns a sanitized copy of the original name with all unsupported characters
+ * replaced with spaces.
+ */
+ @NonNull
+ public String getSanitizedName() {
+ return mSanitizedName;
+ }
+
+ /**
+ * Returns whether the original name isValid.
+ *
+ * <p>If this returns false then inserts and updates using the name will throw an
+ * {@link IllegalArgumentException}
+ */
+ public boolean isValid() {
+ return mMaxEncodedLength > 0 && mEncodedLength <= mMaxEncodedLength
+ && Objects.equals(
+ mName, mSanitizedName);
+ }
+
+ /** Returns whether the character at the specified position is supported by the SIM. */
+ public boolean isSupportedCharacter(int position) {
+ return mName.charAt(position) == mSanitizedName.charAt(position);
+ }
+
+ /**
+ * Returns the number of bytes required to save the name.
+ *
+ * <p>This may be more than the number of characters in the name.
+ */
+ public int getEncodedLength() {
+ return mEncodedLength;
+ }
+
+ /**
+ * Returns the maximum number of bytes that are supported for the name.
+ *
+ * @see ElementaryFiles#NAME_MAX_LENGTH
+ */
+ public int getMaxEncodedLength() {
+ return mMaxEncodedLength;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeString(mSanitizedName);
+ dest.writeInt(mEncodedLength);
+ dest.writeInt(mMaxEncodedLength);
+ }
+ }
+ }
+
+ /** Constants for metadata about the elementary files of the SIM cards in the phone. */
+ public static final class ElementaryFiles {
+
+ /** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */
+ public static final String SLOT_INDEX = "slot_index";
+ /** {@link SubscriptionInfo#getSubscriptionId()} of the SIM for this row. */
+ public static final String SUBSCRIPTION_ID = "subscription_id";
+ /**
+ * The elementary file type for this row.
+ *
+ * @see ElementaryFiles#EF_ADN
+ * @see ElementaryFiles#EF_FDN
+ * @see ElementaryFiles#EF_SDN
+ */
+ public static final String EF_TYPE = "ef_type";
+ /** The maximum number of records supported by the elementary file. */
+ public static final String MAX_RECORDS = "max_records";
+ /** Count of the number of records that are currently stored in the elementary file. */
+ public static final String RECORD_COUNT = "record_count";
+ /** The maximum length supported for the name of a record in the elementary file. */
+ public static final String NAME_MAX_LENGTH = "name_max_length";
+ /**
+ * The maximum length supported for the phone number of a record in the elementary file.
+ */
+ public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+
+ /**
+ * A value for an elementary file that is not recognized.
+ *
+ * <p>Generally this should be ignored. If new values are added then this will be used
+ * for apps that target SDKs where they aren't defined.
+ */
+ public static final int EF_UNKNOWN = 0;
+ /**
+ * Type for accessing records in the "abbreviated dialing number" (ADN) elementary file on
+ * the SIM.
+ *
+ * <p>ADN records are typically user created.
+ */
+ public static final int EF_ADN = 1;
+ /**
+ * Type for accessing records in the "fixed dialing number" (FDN) elementary file on the
+ * SIM.
+ *
+ * <p>FDN numbers are the numbers that are allowed to dialed for outbound calls when FDN is
+ * enabled.
+ *
+ * <p>FDN records cannot be modified by applications. Hence, insert, update and
+ * delete methods operating on this Uri will throw UnsupportedOperationException
+ */
+ public static final int EF_FDN = 2;
+ /**
+ * Type for accessing records in the "service dialing number" (SDN) elementary file on the
+ * SIM.
+ *
+ * <p>Typically SDNs are preset numbers provided by the carrier for common operations (e.g.
+ * voicemail, check balance, etc).
+ *
+ * <p>SDN records cannot be modified by applications. Hence, insert, update and delete
+ * methods operating on this Uri will throw UnsupportedOperationException
+ */
+ public static final int EF_SDN = 3;
+ /** @hide */
+ @SystemApi
+ public static final String EF_ADN_PATH_SEGMENT = "adn";
+ /** @hide */
+ @SystemApi
+ public static final String EF_FDN_PATH_SEGMENT = "fdn";
+ /** @hide */
+ @SystemApi
+ public static final String EF_SDN_PATH_SEGMENT = "sdn";
+ /** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+ /** The MIME type of a CONTENT_URI subdirectory of a single ADN-like elementary file. */
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/sim-elementary-file";
+ /**
+ * The Uri path segment used to construct Uris for the metadata defined in this class.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+
+ /** Content URI for the ADN-like elementary files available on the device. */
+ @NonNull
+ public static final Uri CONTENT_URI = AUTHORITY_URI
+ .buildUpon()
+ .appendPath(ELEMENTARY_FILES_PATH_SEGMENT).build();
+
+ private ElementaryFiles() {
+ }
+
+ /**
+ * Returns a content uri for a specific elementary file.
+ *
+ * <p>If a SIM with the specified subscriptionId is not present an exception will be thrown.
+ * If the SIM doesn't support the specified elementary file it will have a zero value for
+ * {@link #MAX_RECORDS}.
+ */
+ @NonNull
+ public static Uri getItemUri(int subscriptionId, @EfType int efType) {
+ return CONTENT_URI.buildUpon().appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+ .appendPath(String.valueOf(subscriptionId))
+ .appendPath(getEfUriPath(efType))
+ .build();
+ }
+
+ /**
+ * Annotation for the valid elementary file types.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"EF"},
+ value = {EF_UNKNOWN, EF_ADN, EF_FDN, EF_SDN})
+ public @interface EfType {
+ }
+ }
+}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f994d29..c39b8c5 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -80,6 +80,7 @@
public static final int KM_TAG_MIN_SECONDS_BETWEEN_OPS =
Tag.MIN_SECONDS_BETWEEN_OPS; // KM_UINT | 403;
public static final int KM_TAG_MAX_USES_PER_BOOT = Tag.MAX_USES_PER_BOOT; // KM_UINT | 404;
+ public static final int KM_TAG_USAGE_COUNT_LIMIT = Tag.USAGE_COUNT_LIMIT; // KM_UINT | 405;
public static final int KM_TAG_USER_ID = Tag.USER_ID; // KM_UINT | 501;
public static final int KM_TAG_USER_SECURE_ID = Tag.USER_SECURE_ID; // KM_ULONG_REP | 502;
diff --git a/core/java/android/service/attestation/ImpressionAttestationService.java b/core/java/android/service/attestation/ImpressionAttestationService.java
deleted file mode 100644
index 968d533..0000000
--- a/core/java/android/service/attestation/ImpressionAttestationService.java
+++ /dev/null
@@ -1,156 +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.service.attestation;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteCallback;
-
-/**
- * A service that handles generating and verify ImpressionTokens.
- *
- * The service will generate an ImpressionToken based on arguments passed in. Then later that same
- * ImpressionToken can be verified to determine that it was created by the system.
- *
- * @hide
- */
-@SystemApi
-public abstract class ImpressionAttestationService extends Service {
- /** @hide **/
- public static final String EXTRA_IMPRESSION_TOKEN =
- "android.service.attestation.extra.IMPRESSION_TOKEN";
-
- /** @hide **/
- public static final String EXTRA_VERIFICATION_STATUS =
- "android.service.attestation.extra.VERIFICATION_STATUS";
-
- /**
- * Manifest metadata key for the resource string array containing the names of all impression
- * attestation algorithms provided by the service.
- *
- * @hide
- */
- public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
- "android.attestation.available_algorithms";
-
- /**
- * The {@link Intent} action that must be declared as handled by a service in its manifest
- * for the system to recognize it as an impression attestation providing service.
- *
- * @hide
- */
- public static final String SERVICE_INTERFACE =
- "android.service.attestation.ImpressionAttestationService";
-
- private ImpressionAttestationServiceWrapper mWrapper;
- private Handler mHandler;
-
- public ImpressionAttestationService() {
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mWrapper = new ImpressionAttestationServiceWrapper();
- mHandler = new Handler(Looper.getMainLooper(), null, true);
- }
-
- @NonNull
- @Override
- public final IBinder onBind(@NonNull Intent intent) {
- return mWrapper;
- }
-
- /**
- * Generates the impression token that can be used to validate that the system
- * generated the token.
- *
- * @param salt The salt to use when generating the hmac. This should be unique to the
- * caller so the token cannot be verified by any other process.
- * @param screenshot The screenshot buffer for the content to attest.
- * @param bounds The size and position of the content being attested in the window.
- * @param hashAlgorithm The String for the hashing algorithm to use based values in
- * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
- * @return An impression token that can be used to validate information about the content.
- * Returns null when the arguments sent are invalid.
- */
- @Nullable
- public abstract ImpressionToken onGenerateImpressionToken(@NonNull byte[] salt,
- @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
- @NonNull String hashAlgorithm);
-
- /**
- * Call to verify that the impressionToken passed in was generated by the system.
- *
- * @param salt The salt value to use when verifying the hmac. This should be the
- * same value that was passed to
- * {@link #onGenerateImpressionToken(byte[],
- * HardwareBuffer, Rect, String)} to
- * generate the token.
- * @param impressionToken The token to verify that it was generated by the system.
- * @return true if the token can be verified that it was generated by the system.
- */
- public abstract boolean onVerifyImpressionToken(@NonNull byte[] salt,
- @NonNull ImpressionToken impressionToken);
-
- private void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
- String hashAlgorithm, RemoteCallback callback) {
- ImpressionToken impressionToken = onGenerateImpressionToken(salt, screenshot, bounds,
- hashAlgorithm);
- final Bundle data = new Bundle();
- data.putParcelable(EXTRA_IMPRESSION_TOKEN, impressionToken);
- callback.sendResult(data);
- }
-
- private void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
- RemoteCallback callback) {
- boolean verificationStatus = onVerifyImpressionToken(salt, impressionToken);
- final Bundle data = new Bundle();
- data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
- callback.sendResult(data);
- }
-
- private final class ImpressionAttestationServiceWrapper extends
- IImpressionAttestationService.Stub {
- @Override
- public void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
- String hashAlgorithm, RemoteCallback callback) {
- mHandler.sendMessage(
- obtainMessage(ImpressionAttestationService::generateImpressionToken,
- ImpressionAttestationService.this, salt, screenshot, bounds,
- hashAlgorithm, callback));
- }
-
- @Override
- public void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
- RemoteCallback callback) {
- mHandler.sendMessage(obtainMessage(ImpressionAttestationService::verifyImpressionToken,
- ImpressionAttestationService.this, salt, impressionToken, callback));
- }
- }
-}
diff --git a/core/java/android/service/attestation/OWNERS b/core/java/android/service/attestation/OWNERS
deleted file mode 100644
index b9e7b99..0000000
--- a/core/java/android/service/attestation/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chaviw@google.com
-ogunwale@google.com
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 4679c56..e3d0741 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -129,14 +129,14 @@
/** @hide */
@TestApi
- @SuppressLint("ConcreteCollection")
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
public @Nullable ArrayList<AutofillId> getFieldIds() {
return mFieldIds;
}
/** @hide */
@TestApi
- @SuppressLint("ConcreteCollection")
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
public @Nullable ArrayList<AutofillValue> getFieldValues() {
return mFieldValues;
}
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index e35b8b7..ad6316c 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -119,6 +119,7 @@
IoUtils.closeQuietly(control.incremental.cmd);
IoUtils.closeQuietly(control.incremental.pendingReads);
IoUtils.closeQuietly(control.incremental.log);
+ IoUtils.closeQuietly(control.incremental.blocksWritten);
}
}
}
diff --git a/core/java/android/service/dataloader/OWNERS b/core/java/android/service/dataloader/OWNERS
new file mode 100644
index 0000000..7f3906b
--- /dev/null
+++ b/core/java/android/service/dataloader/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/os/incremental/OWNERS
\ No newline at end of file
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index e0f3018..44daeff 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -46,7 +46,7 @@
void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
// assistants only
- void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
+ void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel, in NotificationRankingUpdate update);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
void onNotificationsSeen(in List<String> keys);
void onPanelRevealed(int items);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index cf2152c..1d49a72 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -126,7 +126,7 @@
* {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
*
* @param sbn the new notification
- * @return an adjustment or null to take no action, within 100ms.
+ * @return an adjustment or null to take no action, within 200ms.
*/
abstract public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn);
@@ -135,7 +135,7 @@
*
* @param sbn the new notification
* @param channel the channel the notification was posted to
- * @return an adjustment or null to take no action, within 100ms.
+ * @return an adjustment or null to take no action, within 200ms.
*/
public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn,
@NonNull NotificationChannel channel) {
@@ -143,6 +143,20 @@
}
/**
+ * A notification was posted by an app. Called before post.
+ *
+ * @param sbn the new notification
+ * @param channel the channel the notification was posted to
+ * @param rankingMap The current ranking map that can be used to retrieve ranking information
+ * for active notifications.
+ * @return an adjustment or null to take no action, within 200ms.
+ */
+ public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn,
+ @NonNull NotificationChannel channel, @NonNull RankingMap rankingMap) {
+ return onNotificationEnqueued(sbn, channel);
+ }
+
+ /**
* Implement this method to learn when notifications are removed, how they were interacted with
* before removal, and why they were removed.
* <p>
@@ -316,7 +330,7 @@
private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
@Override
public void onNotificationEnqueuedWithChannel(IStatusBarNotificationHolder sbnHolder,
- NotificationChannel channel) {
+ NotificationChannel channel, NotificationRankingUpdate update) {
StatusBarNotification sbn;
try {
sbn = sbnHolder.get();
@@ -330,9 +344,11 @@
return;
}
+ applyUpdateLocked(update);
SomeArgs args = SomeArgs.obtain();
args.arg1 = sbn;
args.arg2 = channel;
+ args.arg3 = getCurrentRanking();
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
args).sendToTarget();
}
@@ -472,8 +488,9 @@
SomeArgs args = (SomeArgs) msg.obj;
StatusBarNotification sbn = (StatusBarNotification) args.arg1;
NotificationChannel channel = (NotificationChannel) args.arg2;
+ RankingMap ranking = (RankingMap) args.arg3;
args.recycle();
- Adjustment adjustment = onNotificationEnqueued(sbn, channel);
+ Adjustment adjustment = onNotificationEnqueued(sbn, channel, ranking);
setAdjustmentIssuer(adjustment);
if (adjustment != null) {
if (!isBound()) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index c41e599..64cddc3 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1431,7 +1431,8 @@
@Override
public void onNotificationEnqueuedWithChannel(
- IStatusBarNotificationHolder notificationHolder, NotificationChannel channel)
+ IStatusBarNotificationHolder notificationHolder, NotificationChannel channel,
+ NotificationRankingUpdate update)
throws RemoteException {
// no-op in the listener
}
diff --git a/core/java/android/service/attestation/IImpressionAttestationService.aidl b/core/java/android/service/screenshot/IScreenshotHasherService.aidl
similarity index 66%
rename from core/java/android/service/attestation/IImpressionAttestationService.aidl
rename to core/java/android/service/screenshot/IScreenshotHasherService.aidl
index 5ff8f17..d14d147 100644
--- a/core/java/android/service/attestation/IImpressionAttestationService.aidl
+++ b/core/java/android/service/screenshot/IScreenshotHasherService.aidl
@@ -14,43 +14,43 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.service.screenshot;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.RemoteCallback;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
/**
- * Service used to handle impression attestation requests.
+ * Service used to handle ScreenshotHash requests.
*
* @hide
*/
-oneway interface IImpressionAttestationService {
+oneway interface IScreenshotHasherService {
/**
- * Generates the impression token that can be used to validate that the system generated the
+ * Generates the ScreenshotHash token that can be used to validate that the system generated the
* token.
*
* @param salt The salt to use when generating the hmac. This should be unique to the caller so
* the token cannot be verified by any other process.
* @param screenshot The screenshot to generate the hash and add to the token.
- * @param bounds The size and position of the content being attested in the window.
+ * @param bounds The size and position of the content being screenshot in the window.
* @param hashAlgorithm The String for the hashing algorithm to use based on values in
* {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}.
- * @param Callback The callback invoked to send back the impression token.
+ * @param Callback The callback invoked to send back the ScreenshotHash token.
*/
- void generateImpressionToken(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
+ void generateScreenshotHash(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
in String hashAlgorithm, in RemoteCallback callback);
/**
- * Call to verify that the impressionToken passed in was generated by the system. The result
+ * Call to verify that the ScreenshotHash passed in was generated by the system. The result
* will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFICATION_STATUS}.
*
* @param salt The salt value to use when verifying the hmac. This should be the same value that
- * was passed to {@link generateImpressionToken()} to generate the token.
- * @param impressionToken The token to verify that it was generated by the system.
+ * was passed to {@link generateScreenshotHash()} to generate the token.
+ * @param screenshotHash The hash to verify that it was generated by the system.
* @param callback The callback invoked to send back the verification status.
*/
- void verifyImpressionToken(in byte[] salt, in ImpressionToken impressionToken,
+ void verifyScreenshotHash(in byte[] salt, in ScreenshotHash screenshotHash,
in RemoteCallback callback);
}
diff --git a/core/java/android/service/screenshot/OWNERS b/core/java/android/service/screenshot/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/java/android/service/screenshot/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/service/attestation/ImpressionToken.aidl b/core/java/android/service/screenshot/ScreenshotHash.aidl
similarity index 90%
rename from core/java/android/service/attestation/ImpressionToken.aidl
rename to core/java/android/service/screenshot/ScreenshotHash.aidl
index 284a4ba..a7c50db 100644
--- a/core/java/android/service/attestation/ImpressionToken.aidl
+++ b/core/java/android/service/screenshot/ScreenshotHash.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.service.screenshot;
-parcelable ImpressionToken;
\ No newline at end of file
+parcelable ScreenshotHash;
diff --git a/core/java/android/service/attestation/ImpressionToken.java b/core/java/android/service/screenshot/ScreenshotHash.java
similarity index 81%
rename from core/java/android/service/attestation/ImpressionToken.java
rename to core/java/android/service/screenshot/ScreenshotHash.java
index 4355dc0..9ae4192 100644
--- a/core/java/android/service/attestation/ImpressionToken.java
+++ b/core/java/android/service/screenshot/ScreenshotHash.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.service.screenshot;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -25,14 +25,14 @@
import com.android.internal.util.DataClass;
/**
- * The ads impression token used to validate information about what was present on screen.
+ * The screenshot hash used to validate information about what was present on screen.
* @hide
*
* TODO: Remove hide and SystemAPI since this will be a public class
*/
@SystemApi
@DataClass(genToString = true, genAidl = true)
-public final class ImpressionToken implements Parcelable {
+public final class ScreenshotHash implements Parcelable {
/**
* The timestamp when the screenshot was generated.
*/
@@ -42,23 +42,27 @@
* The bounds of the requested area to take the screenshot. This is in window space passed in
* by the client.
*/
- private @NonNull final Rect mBoundsInWindow;
+ @NonNull
+ private final Rect mBoundsInWindow;
/**
* The selected hashing algorithm that generated the image hash.
*/
- private @NonNull final String mHashingAlgorithm;
+ @NonNull
+ private final String mHashingAlgorithm;
/**
- * The image hash generated when creating the impression token from the screenshot taken.
+ * The image hash generated when creating the ScreenshotHash from the screenshot taken.
*/
- private @NonNull final byte[] mImageHash;
+ @NonNull
+ private final byte[] mImageHash;
/**
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*/
- private @NonNull final byte[] mHmac;
+ @NonNull
+ private final byte[] mHmac;
/**
* The hmac generated by the system and used to verify whether this token was generated by
@@ -67,19 +71,21 @@
* @hide
*/
@SystemApi
- public @NonNull byte[] getHmac() {
+ @NonNull
+ public byte[] getHmac() {
return mHmac;
}
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/attestation/ImpressionToken.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/screenshot
+ // /ScreenshotHash.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -87,7 +93,7 @@
/**
- * Creates a new ImpressionToken.
+ * Creates a new ScreenshotHash.
*
* @param screenshotTimeMillis
* The timestamp when the screenshot was generated.
@@ -97,13 +103,13 @@
* @param hashingAlgorithm
* The selected hashing algorithm that generated the image hash.
* @param imageHash
- * The image hash generated when creating the impression token from the screenshot taken.
+ * The image hash generated when creating the ScreenshotHash from the screenshot taken.
* @param hmac
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*/
@DataClass.Generated.Member
- public ImpressionToken(
+ public ScreenshotHash(
long screenshotTimeMillis,
@NonNull Rect boundsInWindow,
@NonNull String hashingAlgorithm,
@@ -152,7 +158,7 @@
}
/**
- * The image hash generated when creating the impression token from the screenshot taken.
+ * The image hash generated when creating the ScreenshotHash from the screenshot taken.
*/
@DataClass.Generated.Member
public @NonNull byte[] getImageHash() {
@@ -165,7 +171,7 @@
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
- return "ImpressionToken { " +
+ return "ScreenshotHash { " +
"screenshotTimeMillis = " + mScreenshotTimeMillis + ", " +
"boundsInWindow = " + mBoundsInWindow + ", " +
"hashingAlgorithm = " + mHashingAlgorithm + ", " +
@@ -194,7 +200,7 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ ImpressionToken(@NonNull Parcel in) {
+ /* package-private */ ScreenshotHash(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -222,24 +228,24 @@
}
@DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<ImpressionToken> CREATOR
- = new Parcelable.Creator<ImpressionToken>() {
+ public static final @NonNull Parcelable.Creator<ScreenshotHash> CREATOR
+ = new Parcelable.Creator<ScreenshotHash>() {
@Override
- public ImpressionToken[] newArray(int size) {
- return new ImpressionToken[size];
+ public ScreenshotHash[] newArray(int size) {
+ return new ScreenshotHash[size];
}
@Override
- public ImpressionToken createFromParcel(@NonNull Parcel in) {
- return new ImpressionToken(in);
+ public ScreenshotHash createFromParcel(@NonNull Parcel in) {
+ return new ScreenshotHash(in);
}
};
@DataClass.Generated(
- time = 1604539951959L,
- codegenVersion = "1.0.18",
- sourceFile = "frameworks/base/core/java/android/service/attestation/ImpressionToken.java",
- inputSignatures = "private final long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ImpressionToken extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
+ time = 1612383172822L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/service/screenshot/ScreenshotHash.java",
+ inputSignatures = "private final long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ScreenshotHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/screenshot/ScreenshotHasherService.java b/core/java/android/service/screenshot/ScreenshotHasherService.java
new file mode 100644
index 0000000..d96cc7e
--- /dev/null
+++ b/core/java/android/service/screenshot/ScreenshotHasherService.java
@@ -0,0 +1,160 @@
+/*
+ * 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.service.screenshot;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+
+/**
+ * A service that handles generating and verify {@link ScreenshotHash}.
+ *
+ * The service will generate a ScreenshotHash based on arguments passed in. Then later that
+ * same ScreenshotHash can be verified to determine that it was created by the system.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class ScreenshotHasherService extends Service {
+ /** @hide **/
+ public static final String EXTRA_SCREENSHOT_HASH =
+ "android.service.screenshot.extra.SCREENSHOT_HASH";
+
+ /** @hide **/
+ public static final String EXTRA_VERIFICATION_STATUS =
+ "android.service.screenshot.extra.VERIFICATION_STATUS";
+
+ /**
+ * Manifest metadata key for the resource string array containing the names of all hashing
+ * algorithms provided by the service.
+ *
+ * @hide
+ */
+ public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
+ "android.screenshot.available_algorithms";
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service in its manifest
+ * for the system to recognize it as a ScreenshotHash providing service.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SERVICE_INTERFACE =
+ "android.service.screenshot.ScreenshotHasherService";
+
+ private ScreenshotHasherServiceWrapper mWrapper;
+ private Handler mHandler;
+
+ public ScreenshotHasherService() {
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWrapper = new ScreenshotHasherServiceWrapper();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @NonNull
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ return mWrapper;
+ }
+
+ /**
+ * Generates the ScreenshotHash that can be used to validate that the system generated the
+ * token.
+ *
+ * @param salt The salt to use when generating the hmac. This should be unique to the
+ * caller so the token cannot be verified by any other process.
+ * @param screenshot The screenshot buffer for the content.
+ * @param bounds The size and position of the content being screenshot in the window.
+ * @param hashAlgorithm The String for the hashing algorithm to use based values in
+ * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
+ * @return A ScreenshotHash that can be used to validate information about the content.
+ * Returns null when the arguments sent are invalid.
+ */
+ @Nullable
+ public abstract ScreenshotHash onGenerateScreenshotHash(@NonNull byte[] salt,
+ @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
+ @NonNull String hashAlgorithm);
+
+ /**
+ * Call to verify that the ScreenshotHash passed in was generated by the system.
+ *
+ * @param salt The salt value to use when verifying the hmac. This should be the
+ * same value that was passed to
+ * {@link #onGenerateScreenshotHash(byte[],
+ * HardwareBuffer, Rect, String)} to
+ * generate the token.
+ * @param screenshotHash The token to verify that it was generated by the system.
+ * @return true if the token can be verified that it was generated by the system.
+ */
+ public abstract boolean onVerifyScreenshotHash(@NonNull byte[] salt,
+ @NonNull ScreenshotHash screenshotHash);
+
+ private void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
+ String hashAlgorithm, RemoteCallback callback) {
+ ScreenshotHash screenshotHash = onGenerateScreenshotHash(salt, screenshot,
+ bounds,
+ hashAlgorithm);
+ final Bundle data = new Bundle();
+ data.putParcelable(EXTRA_SCREENSHOT_HASH, screenshotHash);
+ callback.sendResult(data);
+ }
+
+ private void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
+ RemoteCallback callback) {
+ boolean verificationStatus = onVerifyScreenshotHash(salt, screenshotHash);
+ final Bundle data = new Bundle();
+ data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
+ callback.sendResult(data);
+ }
+
+ private final class ScreenshotHasherServiceWrapper extends
+ IScreenshotHasherService.Stub {
+ @Override
+ public void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
+ String hashAlgorithm, RemoteCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(ScreenshotHasherService::generateScreenshotHash,
+ ScreenshotHasherService.this, salt, screenshot, bounds,
+ hashAlgorithm, callback));
+ }
+
+ @Override
+ public void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
+ RemoteCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(ScreenshotHasherService::verifyScreenshotHash,
+ ScreenshotHasherService.this, salt, screenshotHash,
+ callback));
+ }
+ }
+}
diff --git a/core/java/android/service/smartspace/ISmartspaceService.aidl b/core/java/android/service/smartspace/ISmartspaceService.aidl
new file mode 100644
index 0000000..c9c6807
--- /dev/null
+++ b/core/java/android/service/smartspace/ISmartspaceService.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.service.smartspace;
+
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.ISmartspaceCallback;
+import android.content.pm.ParceledListSlice;
+
+/**
+ * Interface from the system to Smartspace service.
+ *
+ * @hide
+ */
+oneway interface ISmartspaceService {
+
+ void onCreateSmartspaceSession(in SmartspaceConfig context, in SmartspaceSessionId sessionId);
+
+ void notifySmartspaceEvent(in SmartspaceSessionId sessionId, in SmartspaceTargetEvent event);
+
+ void requestSmartspaceUpdate(in SmartspaceSessionId sessionId);
+
+ void registerSmartspaceUpdates(in SmartspaceSessionId sessionId,
+ in ISmartspaceCallback callback);
+
+ void unregisterSmartspaceUpdates(in SmartspaceSessionId sessionId,
+ in ISmartspaceCallback callback);
+
+ void onDestroySmartspaceSession(in SmartspaceSessionId sessionId);
+}
diff --git a/core/java/android/service/smartspace/OWNERS b/core/java/android/service/smartspace/OWNERS
new file mode 100644
index 0000000..19ef9d7
--- /dev/null
+++ b/core/java/android/service/smartspace/OWNERS
@@ -0,0 +1,2 @@
+srazdan@google.com
+alexmang@google.com
\ No newline at end of file
diff --git a/core/java/android/service/smartspace/SmartspaceService.java b/core/java/android/service/smartspace/SmartspaceService.java
new file mode 100644
index 0000000..09b7310
--- /dev/null
+++ b/core/java/android/service/smartspace/SmartspaceService.java
@@ -0,0 +1,313 @@
+/*
+ * 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.service.smartspace;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.smartspace.ISmartspaceCallback;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.service.smartspace.ISmartspaceService.Stub;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A service used to share the lifecycle of smartspace UI (open, close, interaction)
+ * and also to return smartspace result on a query.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class SmartspaceService extends Service {
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ *
+ * <p>The service must also require the {@link android.permission#MANAGE_SMARTSPACE}
+ * permission.
+ *
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.smartspace.SmartspaceService";
+ private static final boolean DEBUG = false;
+ private static final String TAG = "SmartspaceService";
+ private final ArrayMap<SmartspaceSessionId, ArrayList<CallbackWrapper>> mSessionCallbacks =
+ new ArrayMap<>();
+ private Handler mHandler;
+
+ private final android.service.smartspace.ISmartspaceService mInterface = new Stub() {
+
+ @Override
+ public void onCreateSmartspaceSession(SmartspaceConfig smartspaceConfig,
+ SmartspaceSessionId sessionId) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doCreateSmartspaceSession,
+ SmartspaceService.this, smartspaceConfig, sessionId));
+ }
+
+ @Override
+ public void notifySmartspaceEvent(SmartspaceSessionId sessionId,
+ SmartspaceTargetEvent event) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::notifySmartspaceEvent,
+ SmartspaceService.this, sessionId, event));
+ }
+
+ @Override
+ public void requestSmartspaceUpdate(SmartspaceSessionId sessionId) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doRequestPredictionUpdate,
+ SmartspaceService.this, sessionId));
+ }
+
+ @Override
+ public void registerSmartspaceUpdates(SmartspaceSessionId sessionId,
+ ISmartspaceCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doRegisterSmartspaceUpdates,
+ SmartspaceService.this, sessionId, callback));
+ }
+
+ @Override
+ public void unregisterSmartspaceUpdates(SmartspaceSessionId sessionId,
+ ISmartspaceCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doUnregisterSmartspaceUpdates,
+ SmartspaceService.this, sessionId, callback));
+ }
+
+ @Override
+ public void onDestroySmartspaceSession(SmartspaceSessionId sessionId) {
+
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doDestroy,
+ SmartspaceService.this, sessionId));
+ }
+ };
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(TAG, "onCreate mSessionCallbacks: " + mSessionCallbacks);
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @Override
+ @NonNull
+ public final IBinder onBind(@NonNull Intent intent) {
+ Log.d(TAG, "onBind mSessionCallbacks: " + mSessionCallbacks);
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Slog.w(TAG, "Tried to bind to wrong intent (should be "
+ + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+
+ private void doCreateSmartspaceSession(@NonNull SmartspaceConfig config,
+ @NonNull SmartspaceSessionId sessionId) {
+ Log.d(TAG, "doCreateSmartspaceSession mSessionCallbacks: " + mSessionCallbacks);
+ mSessionCallbacks.put(sessionId, new ArrayList<>());
+ onCreateSmartspaceSession(config, sessionId);
+ }
+
+ /**
+ * Gets called when the client calls <code> SmartspaceManager#createSmartspaceSession </code>.
+ */
+ public abstract void onCreateSmartspaceSession(@NonNull SmartspaceConfig config,
+ @NonNull SmartspaceSessionId sessionId);
+
+ /**
+ * Gets called when the client calls <code> SmartspaceSession#notifySmartspaceEvent </code>.
+ */
+ @MainThread
+ public abstract void notifySmartspaceEvent(@NonNull SmartspaceSessionId sessionId,
+ @NonNull SmartspaceTargetEvent event);
+
+ /**
+ * Gets called when the client calls <code> SmartspaceSession#requestSmartspaceUpdate </code>.
+ */
+ @MainThread
+ public abstract void onRequestSmartspaceUpdate(@NonNull SmartspaceSessionId sessionId);
+
+ private void doRegisterSmartspaceUpdates(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ Log.d(TAG, "doRegisterSmartspaceUpdates mSessionCallbacks: " + mSessionCallbacks);
+ final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+ if (callbacks == null) {
+ Slog.e(TAG, "Failed to register for updates for unknown session: " + sessionId);
+ return;
+ }
+
+ final CallbackWrapper wrapper = findCallbackWrapper(callbacks, callback);
+ if (wrapper == null) {
+ callbacks.add(new CallbackWrapper(callback,
+ callbackWrapper ->
+ mHandler.post(
+ () -> removeCallbackWrapper(callbacks, callbackWrapper))));
+ }
+ }
+
+ private void doUnregisterSmartspaceUpdates(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ Log.d(TAG, "doUnregisterSmartspaceUpdates mSessionCallbacks: " + mSessionCallbacks);
+ final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+ if (callbacks == null) {
+ Slog.e(TAG, "Failed to unregister for updates for unknown session: " + sessionId);
+ return;
+ }
+
+ final CallbackWrapper wrapper = findCallbackWrapper(callbacks, callback);
+ if (wrapper != null) {
+ removeCallbackWrapper(callbacks, wrapper);
+ }
+ }
+
+ private void doRequestPredictionUpdate(@NonNull SmartspaceSessionId sessionId) {
+ Log.d(TAG, "doRequestPredictionUpdate mSessionCallbacks: " + mSessionCallbacks);
+ // Just an optimization, if there are no callbacks, then don't bother notifying the service
+ final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+ if (callbacks != null && !callbacks.isEmpty()) {
+ onRequestSmartspaceUpdate(sessionId);
+ }
+ }
+
+ /**
+ * Finds the callback wrapper for the given callback.
+ */
+ private CallbackWrapper findCallbackWrapper(ArrayList<CallbackWrapper> callbacks,
+ ISmartspaceCallback callback) {
+ for (int i = callbacks.size() - 1; i >= 0; i--) {
+ if (callbacks.get(i).isCallback(callback)) {
+ return callbacks.get(i);
+ }
+ }
+ return null;
+ }
+
+ private void removeCallbackWrapper(
+ ArrayList<CallbackWrapper> callbacks, CallbackWrapper wrapper) {
+ if (callbacks == null) {
+ return;
+ }
+ callbacks.remove(wrapper);
+ }
+
+ /**
+ * Gets called when the client calls <code> SmartspaceManager#destroy() </code>.
+ */
+ public abstract void onDestroySmartspaceSession(@NonNull SmartspaceSessionId sessionId);
+
+ private void doDestroy(@NonNull SmartspaceSessionId sessionId) {
+ Log.d(TAG, "doDestroy mSessionCallbacks: " + mSessionCallbacks);
+ super.onDestroy();
+ mSessionCallbacks.remove(sessionId);
+ onDestroySmartspaceSession(sessionId);
+ }
+
+ /**
+ * Used by the prediction factory to send back results the client app. The can be called
+ * in response to {@link #onRequestSmartspaceUpdate(SmartspaceSessionId)} or proactively as
+ * a result of changes in predictions.
+ */
+ public final void updateSmartspaceTargets(@NonNull SmartspaceSessionId sessionId,
+ @NonNull List<SmartspaceTarget> targets) {
+ Log.d(TAG, "updateSmartspaceTargets mSessionCallbacks: " + mSessionCallbacks);
+ List<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+ if (callbacks != null) {
+ for (CallbackWrapper callback : callbacks) {
+ callback.accept(targets);
+ }
+ }
+ }
+
+ /**
+ * Destroys a smartspace session.
+ */
+ @MainThread
+ public abstract void onDestroy(@NonNull SmartspaceSessionId sessionId);
+
+ private static final class CallbackWrapper implements Consumer<List<SmartspaceTarget>>,
+ IBinder.DeathRecipient {
+
+ private final Consumer<CallbackWrapper> mOnBinderDied;
+ private ISmartspaceCallback mCallback;
+
+ CallbackWrapper(ISmartspaceCallback callback,
+ @Nullable Consumer<CallbackWrapper> onBinderDied) {
+ mCallback = callback;
+ mOnBinderDied = onBinderDied;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death: " + e);
+ }
+ }
+
+ public boolean isCallback(@NonNull ISmartspaceCallback callback) {
+ if (mCallback == null) {
+ Slog.e(TAG, "Callback is null, likely the binder has died.");
+ return false;
+ }
+ return mCallback.equals(callback);
+ }
+
+ @Override
+ public void accept(List<SmartspaceTarget> smartspaceTargets) {
+ try {
+ if (mCallback != null) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "CallbackWrapper.accept smartspaceTargets=" + smartspaceTargets);
+ }
+ mCallback.onResult(new ParceledListSlice(smartspaceTargets));
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result:" + e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ mCallback = null;
+ if (mOnBinderDied != null) {
+ mOnBinderDied.accept(this);
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 68d6f3f..25f8090 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -345,7 +345,8 @@
*/
@SystemApi
@HotwordConfigResult
- public final int setHotwordDetectionConfig(@Nullable Bundle options) {
+ public final int setHotwordDetectionConfig(
+ @SuppressLint("NullableCollection") @Nullable Bundle options) {
if (mSystemService == null) {
throw new IllegalStateException("Not available until onReady() is called");
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 507dc7a..82e0b4a 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -880,8 +880,8 @@
InputChannel inputChannel = new InputChannel();
if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
- mDisplay.getDisplayId(), mInsetsState, mWinFrames.frame,
- inputChannel, mInsetsState, mTempControls) < 0) {
+ mDisplay.getDisplayId(), mInsetsState, inputChannel, mInsetsState,
+ mTempControls) < 0) {
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
return;
}
@@ -1494,6 +1494,16 @@
private void doDetachEngine() {
mActiveEngines.remove(mEngine);
mEngine.detach();
+ // Some wallpapers will not trigger the rendering threads of the remaining engines even
+ // if they are visible, so we need to toggle the state to get their attention.
+ if (!mDetached.get()) {
+ for (Engine eng : mActiveEngines) {
+ if (eng.mVisible) {
+ eng.doVisibilityChanged(false);
+ eng.doVisibilityChanged(true);
+ }
+ }
+ }
}
@Override
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 53fe1ba..2de7558 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -53,7 +53,7 @@
public final class FontConfig implements Parcelable {
private final @NonNull List<FontFamily> mFamilies;
private final @NonNull List<Alias> mAliases;
- private final long mLastModifiedDate;
+ private final long mLastModifiedTimeMillis;
private final int mConfigVersion;
/**
@@ -65,10 +65,10 @@
* @hide Only system server can create this instance and passed via IPC.
*/
public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases,
- long lastModifiedDate, @IntRange(from = 0) int configVersion) {
+ long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) {
mFamilies = families;
mAliases = aliases;
- mLastModifiedDate = lastModifiedDate;
+ mLastModifiedTimeMillis = lastModifiedTimeMillis;
mConfigVersion = configVersion;
}
@@ -93,20 +93,21 @@
}
/**
- * Returns the last modified date as Java epoch seconds.
+ * Returns the last modified time in milliseconds.
+ *
+ * This is a value of {@link System#currentTimeMillis()} when the system font configuration was
+ * modified last time.
*
* If there is no update, this return 0.
- * @hide
*/
- public long getLastModifiedDate() {
- return mLastModifiedDate;
+ public long getLastModifiedTimeMillis() {
+ return mLastModifiedTimeMillis;
}
/**
* Returns the monotonically increasing config version value.
*
* The config version is reset to 0 when the system is restarted.
- * @hide
*/
public @IntRange(from = 0) int getConfigVersion() {
return mConfigVersion;
@@ -132,7 +133,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelableList(mFamilies, flags);
dest.writeParcelableList(mAliases, flags);
- dest.writeLong(mLastModifiedDate);
+ dest.writeLong(mLastModifiedTimeMillis);
dest.writeInt(mConfigVersion);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b229212..790773f 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@
DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
- DEFAULT_FLAGS.put("settings_silky_home", "false");
+ DEFAULT_FLAGS.put("settings_silky_home", "true");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
}
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java
index 723f1dd..49ff237 100644
--- a/core/java/android/util/imetracing/ImeTracing.java
+++ b/core/java/android/util/imetracing/ImeTracing.java
@@ -28,6 +28,8 @@
import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.inputmethod.Completable;
+import com.android.internal.inputmethod.ResultCallbacks;
import com.android.internal.view.IInputMethodManager;
import java.io.PrintWriter;
@@ -91,7 +93,9 @@
* @param where
*/
public void sendToService(byte[] protoDump, int source, String where) throws RemoteException {
- mService.startProtoDump(protoDump, source, where);
+ final Completable.Void value = Completable.createVoid();
+ mService.startProtoDump(protoDump, source, where, ResultCallbacks.of(value));
+ Completable.getResult(value);
}
/**
diff --git a/core/java/android/util/imetracing/OWNERS b/core/java/android/util/imetracing/OWNERS
new file mode 100644
index 0000000..885fd0a
--- /dev/null
+++ b/core/java/android/util/imetracing/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java
index bc66ea1..547bc9d 100644
--- a/core/java/android/view/ContentInfo.java
+++ b/core/java/android/view/ContentInfo.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -204,6 +205,7 @@
* the IME.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public Bundle getExtras() {
return mExtras;
}
@@ -347,7 +349,7 @@
* @return this builder
*/
@NonNull
- public Builder setExtras(@Nullable Bundle extras) {
+ public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
mExtras = extras;
return this;
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index c664ccb..4168064 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -25,8 +25,8 @@
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.KeyguardManager;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -59,12 +59,8 @@
* an application window, excluding the system decorations. The application display area may
* be smaller than the real display area because the system subtracts the space needed
* for decor elements such as the status bar. Use {@link WindowMetrics#getBounds()} to query the
- * application window bounds.</li>
- * <li>The real display area specifies the part of the display that contains content
- * including the system decorations. Even so, the real display area may be smaller than the
- * physical size of the display if the window manager is emulating a smaller display
- * using (adb shell wm size). Use the following methods to query the
- * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li>
+ * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to
+ * query the metrics and perform UI-related actions.</li>
* </ul>
* </p><p>
* A logical display does not necessarily represent a particular physical display device
@@ -677,9 +673,9 @@
@UnsupportedAppUsage
public DisplayAdjustments getDisplayAdjustments() {
if (mResources != null) {
- final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
- if (!mDisplayAdjustments.equals(currentAdjustements)) {
- mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
+ final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
+ if (!mDisplayAdjustments.equals(currentAdjustments)) {
+ mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
}
}
@@ -900,6 +896,32 @@
}
/**
+ * Returns the {@link RoundedCorner} of the given position if there is one.
+ *
+ * @param position the position of the rounded corner on the display.
+ *
+ * @return the rounded corner of the given position. Returns {@code null} if there is none.
+ */
+ @SuppressLint("VisiblySynchronized")
+ @Nullable
+ public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ RoundedCorners roundedCorners;
+ if (mMayAdjustByFixedRotation) {
+ roundedCorners = getDisplayAdjustments().adjustRoundedCorner(
+ mDisplayInfo.roundedCorners,
+ mDisplayInfo.rotation,
+ mDisplayInfo.logicalWidth,
+ mDisplayInfo.logicalHeight);
+ } else {
+ roundedCorners = mDisplayInfo.roundedCorners;
+ }
+ return roundedCorners == null ? null : roundedCorners.getRoundedCorner(position);
+ }
+ }
+
+ /**
* Gets the pixel format of the display.
* @return One of the constants defined in {@link android.graphics.PixelFormat}.
*
@@ -1191,30 +1213,34 @@
}
/**
- * Gets the real size of the display without subtracting any window decor or
- * applying any compatibility scale factors.
+ * Provides the largest {@link Point outSize} an app may expect in the current system state,
+ * without subtracting any window decor.
* <p>
- * The size is adjusted based on the current rotation of the display.
+ * The size describes the largest potential area the window might occupy. The size is adjusted
+ * based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
- * </p><p>
- * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
- * report the same bounds except that certain areas of the display may not be available to
- * windows created in the {@link WindowManager}'s {@link Context}.
- *
- * For example, imagine a device which has a multi-task mode that limits windows to half of the
- * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
- * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
- * still reports the bounds of the whole display.
+ * </p>
*
* @param outSize Set to the real size of the display.
- *
- * @see WindowManager#getMaximumWindowMetrics()
*/
public void getRealSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
+ if (shouldReportMaxBounds()) {
+ final Rect bounds = mResources.getConfiguration()
+ .windowConfiguration.getMaxBounds();
+ outSize.x = bounds.width();
+ outSize.y = bounds.height();
+ if (DEBUG) {
+ Log.d(TAG, "getRealSize determined from max bounds: " + outSize
+ + " for uid " + Process.myUid());
+ }
+ // Skip adjusting by fixed rotation, since if it is necessary, the configuration
+ // should already reflect the expected rotation.
+ return;
+ }
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
if (mMayAdjustByFixedRotation) {
@@ -1224,9 +1250,11 @@
}
/**
- * Gets display metrics based on the real size of this display.
+ * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current
+ * system state, without subtracting any window decor.
* <p>
- * The size is adjusted based on the current rotation of the display.
+ * The size describes the largest potential area the window might occupy. The size is adjusted
+ * based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
@@ -1237,6 +1265,18 @@
public void getRealMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
+ if (shouldReportMaxBounds()) {
+ mDisplayInfo.getMaxBoundsMetrics(outMetrics,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
+ mResources.getConfiguration());
+ if (DEBUG) {
+ Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics
+ + " for uid " + Process.myUid());
+ }
+ // Skip adjusting by fixed rotation, since if it is necessary, the configuration
+ // should already reflect the expected rotation.
+ return;
+ }
mDisplayInfo.getLogicalMetrics(outMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
if (mMayAdjustByFixedRotation) {
@@ -1246,6 +1286,20 @@
}
/**
+ * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
+ * display dimensions. The max bounds field may be smaller than the logical dimensions
+ * when apps need to be sandboxed.
+ * @return {@code true} when max bounds should be applied.
+ */
+ private boolean shouldReportMaxBounds() {
+ if (mResources == null) {
+ return false;
+ }
+ final Configuration config = mResources.getConfiguration();
+ return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
+ }
+
+ /**
* Gets the state of the display, such as whether it is on or off.
*
* @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index 5d5771c..e307eff 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -151,6 +151,23 @@
: realCutout;
}
+ /**
+ * Returns the adjusted {@link RoundedCorners} if available. Otherwise the original
+ * {@link RoundedCorners} is returned.
+ */
+ @Nullable
+ public RoundedCorners adjustRoundedCorner(@Nullable RoundedCorners realRoundedCorners,
+ @Surface.Rotation int realRotation, int displayWidth, int displayHeight) {
+ final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
+ if (realRoundedCorners == null || rotationAdjustments == null
+ || rotationAdjustments.mRotation == realRotation) {
+ return realRoundedCorners;
+ }
+
+ return realRoundedCorners.rotate(
+ rotationAdjustments.mRotation, displayWidth, displayHeight);
+ }
+
/** Returns the adjusted rotation if available. Otherwise the original rotation is returned. */
@Surface.Rotation
public int getRotation(@Surface.Rotation int realRotation) {
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index d200a328..8a44504 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,6 +24,7 @@
import static android.view.DisplayInfoProto.NAME;
import android.annotation.Nullable;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -301,6 +302,12 @@
*/
public float brightnessDefault;
+ /**
+ * The {@link RoundedCorners} if present, otherwise {@code null}.
+ */
+ @Nullable
+ public RoundedCorners roundedCorners;
+
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
@@ -369,7 +376,8 @@
&& refreshRateOverride == other.refreshRateOverride
&& brightnessMinimum == other.brightnessMinimum
&& brightnessMaximum == other.brightnessMaximum
- && brightnessDefault == other.brightnessDefault;
+ && brightnessDefault == other.brightnessDefault
+ && Objects.equals(roundedCorners, other.roundedCorners);
}
@Override
@@ -418,6 +426,7 @@
brightnessMinimum = other.brightnessMinimum;
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
+ roundedCorners = other.roundedCorners;
}
public void readFromParcel(Parcel source) {
@@ -468,6 +477,7 @@
brightnessMinimum = source.readFloat();
brightnessMaximum = source.readFloat();
brightnessDefault = source.readFloat();
+ roundedCorners = source.readTypedObject(RoundedCorners.CREATOR);
}
@Override
@@ -517,6 +527,7 @@
dest.writeFloat(brightnessMinimum);
dest.writeFloat(brightnessMaximum);
dest.writeFloat(brightnessDefault);
+ dest.writeTypedObject(roundedCorners, flags);
}
@Override
@@ -605,11 +616,29 @@
getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
}
+ /**
+ * Populates {@code outMetrics} with details of the logical display. Bounds are limited
+ * by the logical size of the display.
+ *
+ * @param outMetrics the {@link DisplayMetrics} to be populated
+ * @param compatInfo the {@link CompatibilityInfo} to be applied
+ * @param configuration the {@link Configuration}
+ */
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration) {
getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
}
+ /**
+ * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
+ * {@link WindowConfiguration#getMaxBounds()}
+ */
+ public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
+ Configuration configuration) {
+ Rect bounds = configuration.windowConfiguration.getMaxBounds();
+ getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
+ }
+
public int getNaturalWidth() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalWidth : logicalHeight;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 68a6de8..7843411 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,7 +32,7 @@
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
@@ -765,21 +765,21 @@
/**
* Gets an array of support hashing algorithms that can be used to generate the hash of the
* screenshot. The String value of one algorithm should be used when requesting to generate
- * the impression attestation token.
+ * the ScreenshotHash.
*
* @return a String array of supported hashing algorithms.
*/
- String[] getSupportedImpressionAlgorithms();
+ String[] getSupportedScreenshotHashingAlgorithms();
/**
- * Validate the impression token was generated by the system. The impression token passed in
- * should be the token generated when calling {@link IWindowSession#generateImpressionToken}
+ * Validate the ScreenshotHash was generated by the system. The ScreenshotHash passed in
+ * should be the token generated when calling {@link IWindowSession#generateScreenshotHash}
*
- * @param impressionToken The token to verify that it was generated by the system.
+ * @param ScreenshotHash The token to verify that it was generated by the system.
* @return true if the token was generated by the system or false if the token cannot be
* verified.
*/
- boolean verifyImpressionToken(in ImpressionToken impressionToken);
+ boolean verifyScreenshotHash(in ScreenshotHash screenshotHash);
/**
* Registers a listener for a {@link android.app.WindowContext} to handle configuration changes
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 85498cb..990b7bd 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,7 +22,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.InputChannel;
@@ -47,11 +47,11 @@
interface IWindowSession {
int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility,
- out Rect outFrame, out InputChannel outInputChannel, out InsetsState insetsState,
+ out InputChannel outInputChannel, out InsetsState insetsState,
out InsetsSourceControl[] activeControls);
int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in int userId,
- in InsetsState requestedVisibility, out Rect outFrame, out InputChannel outInputChannel,
+ in InsetsState requestedVisibility, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out InsetsState insetsState);
@@ -345,14 +345,14 @@
void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus);
/**
- * Generates an impression token that can be used to validate whether specific content was on
+ * Generates an ScreenshotHash that can be used to validate whether specific content was on
* screen.
*
- * @param window The token for the window where the view to attest is shown.
+ * @param window The token for the window where the view to screenshot is shown.
* @param boundsInWindow The size and position of the ads view in the window
* @param hashAlgorithm The String for the hashing algorithm to use based on values returned
- * from {@link IWindowManager#getSupportedImpressionAlgorithms()}
+ * from {@link IWindowManager#getSupportedHashingAlgorithms()}
*/
- ImpressionToken generateImpressionToken(IWindow window, in Rect boundsInWindow,
+ ScreenshotHash generateScreenshotHash(IWindow window, in Rect boundsInWindow,
in String hashAlgorithm);
}
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index f4b90e1..59e4931 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -22,6 +22,7 @@
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.hardware.Battery;
import android.hardware.SensorManager;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
@@ -73,6 +74,7 @@
private final boolean mHasMicrophone;
private final boolean mHasButtonUnderPad;
private final boolean mHasSensor;
+ private final boolean mHasBattery;
private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
@GuardedBy("mMotionRanges")
@@ -84,6 +86,9 @@
@GuardedBy("mMotionRanges")
private SensorManager mSensorManager;
+ @GuardedBy("mMotionRanges")
+ private Battery mBattery;
+
/**
* A mask for input source classes.
*
@@ -323,6 +328,13 @@
public static final int SOURCE_HDMI = 0x02000000 | SOURCE_CLASS_BUTTON;
/**
+ * The input source is a sensor associated with the input device.
+ *
+ * @see #SOURCE_CLASS_NONE
+ */
+ public static final int SOURCE_SENSOR = 0x04000000 | SOURCE_CLASS_NONE;
+
+ /**
* A special input source constant that is used when filtering input devices
* to match devices that provide any type of input source.
*/
@@ -443,12 +455,11 @@
* Called by native code
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@VisibleForTesting
public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasMicrophone,
- boolean hasButtonUnderPad, boolean hasSensor) {
+ boolean hasButtonUnderPad, boolean hasSensor, boolean hasBattery) {
mId = id;
mGeneration = generation;
mControllerNumber = controllerNumber;
@@ -464,6 +475,7 @@
mHasMicrophone = hasMicrophone;
mHasButtonUnderPad = hasButtonUnderPad;
mHasSensor = hasSensor;
+ mHasBattery = hasBattery;
mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId);
}
@@ -483,6 +495,7 @@
mHasMicrophone = in.readInt() != 0;
mHasButtonUnderPad = in.readInt() != 0;
mHasSensor = in.readInt() != 0;
+ mHasBattery = in.readInt() != 0;
mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId);
int numRanges = in.readInt();
@@ -830,6 +843,22 @@
}
/**
+ * Gets the battery object associated with the device, if there is one.
+ * Even if the device does not have a battery, the result is never null.
+ * Use {@link Battery#hasBattery} to determine whether a battery is
+ * present.
+ *
+ * @return The battery object associated with the device, never null.
+ */
+ @NonNull
+ public Battery getBattery() {
+ if (mBattery == null) {
+ mBattery = InputManager.getInstance().getInputDeviceBattery(mId, mHasBattery);
+ }
+ return mBattery;
+ }
+
+ /**
* 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.
@@ -1051,6 +1080,7 @@
out.writeInt(mHasMicrophone ? 1 : 0);
out.writeInt(mHasButtonUnderPad ? 1 : 0);
out.writeInt(mHasSensor ? 1 : 0);
+ out.writeInt(mHasBattery ? 1 : 0);
final int numRanges = mMotionRanges.size();
out.writeInt(numRanges);
@@ -1097,6 +1127,8 @@
description.append(" Has Sensor: ").append(mHasSensor).append("\n");
+ description.append(" Has battery: ").append(mHasBattery).append("\n");
+
description.append(" Has mic: ").append(mHasMicrophone).append("\n");
description.append(" Sources: 0x").append(Integer.toHexString(mSources)).append(" (");
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b4e1172..e681c0e 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -653,6 +653,7 @@
private void updateState(InsetsState newState) {
mState.setDisplayFrame(newState.getDisplayFrame());
mState.setDisplayCutout(newState.getDisplayCutout());
+ mState.setRoundedCorners(newState.getRoundedCorners());
@InsetsType int disabledUserAnimationTypes = 0;
@InsetsType int[] cancelledUserAnimationTypes = {0};
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java
index a334907..3355252 100644
--- a/core/java/android/view/InsetsFlags.java
+++ b/core/java/android/view/InsetsFlags.java
@@ -21,6 +21,8 @@
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -54,7 +56,15 @@
@ViewDebug.FlagToString(
mask = APPEARANCE_LIGHT_NAVIGATION_BARS,
equals = APPEARANCE_LIGHT_NAVIGATION_BARS,
- name = "LIGHT_NAVIGATION_BARS")
+ name = "LIGHT_NAVIGATION_BARS"),
+ @ViewDebug.FlagToString(
+ mask = APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS,
+ equals = APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS,
+ name = "SEMI_TRANSPARENT_STATUS_BARS"),
+ @ViewDebug.FlagToString(
+ mask = APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS,
+ equals = APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS,
+ name = "SEMI_TRANSPARENT_NAVIGATION_BARS")
})
public @Appearance int appearance;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index bf377b0..fe6b6e4 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -106,7 +106,9 @@
public static final int ITYPE_NAVIGATION_BAR = 1;
public static final int ITYPE_CAPTION_BAR = 2;
- public static final int ITYPE_TOP_GESTURES = 3;
+ // 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_BOTTOM_GESTURES = 4;
public static final int ITYPE_LEFT_GESTURES = 5;
public static final int ITYPE_RIGHT_GESTURES = 6;
@@ -117,15 +119,16 @@
public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;
- public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 11;
- public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 12;
- public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 13;
- public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 14;
+ public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11;
+ 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_DISPLAY_CUTOUT = 15;
- public static final int ITYPE_TOP_DISPLAY_CUTOUT = 16;
- public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 17;
- public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 18;
+ public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
+ public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
+ public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17;
+ public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18;
/** Input method window. */
public static final int ITYPE_IME = 19;
@@ -170,6 +173,9 @@
private final DisplayCutout.ParcelableWrapper mDisplayCutout =
new DisplayCutout.ParcelableWrapper();
+ /** The rounded corners on the display */
+ private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
+
public InsetsState() {
}
@@ -182,6 +188,18 @@
}
/**
+ * 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.
@@ -241,7 +259,8 @@
}
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
- alwaysConsumeSystemBars, calculateRelativeCutout(frame), compatInsetsTypes,
+ alwaysConsumeSystemBars, calculateRelativeCutout(frame),
+ calculateRelativeRoundedCorners(frame), compatInsetsTypes,
(legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
}
@@ -266,6 +285,20 @@
return raw.inset(insetLeft, insetTop, insetRight, insetBottom);
}
+ private RoundedCorners calculateRelativeRoundedCorners(Rect frame) {
+ if (mDisplayFrame.equals(frame)) {
+ return mRoundedCorners;
+ }
+ if (frame == null) {
+ return RoundedCorners.NO_ROUNDED_CORNERS;
+ }
+ final int insetLeft = frame.left - mDisplayFrame.left;
+ final int insetTop = frame.top - mDisplayFrame.top;
+ final int insetRight = mDisplayFrame.right - frame.right;
+ final int insetBottom = mDisplayFrame.bottom - frame.bottom;
+ return mRoundedCorners.inset(insetLeft, insetTop, insetRight, insetBottom);
+ }
+
public Rect calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
Insets insets = Insets.NONE;
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
@@ -447,6 +480,14 @@
return mDisplayCutout.get();
}
+ public void setRoundedCorners(RoundedCorners roundedCorners) {
+ mRoundedCorners = roundedCorners;
+ }
+
+ public RoundedCorners getRoundedCorners() {
+ return mRoundedCorners;
+ }
+
/**
* Modifies the state of this class to exclude a certain type to make it ready for dispatching
* to the client.
@@ -478,6 +519,7 @@
public void scale(float scale) {
mDisplayFrame.scale(scale);
mDisplayCutout.scale(scale);
+ mRoundedCorners = mRoundedCorners.scale(scale);
for (int i = 0; i < SIZE; i++) {
final InsetsSource source = mSources[i];
if (source != null) {
@@ -497,6 +539,7 @@
public void set(InsetsState other, boolean copySources) {
mDisplayFrame.set(other.mDisplayFrame);
mDisplayCutout.set(other.mDisplayCutout);
+ mRoundedCorners = other.getRoundedCorners();
if (copySources) {
for (int i = 0; i < SIZE; i++) {
InsetsSource source = other.mSources[i];
@@ -604,11 +647,15 @@
}
public void dump(String prefix, PrintWriter pw) {
+ final String newPrefix = prefix + " ";
pw.println(prefix + "InsetsState");
+ pw.println(newPrefix + "mDisplayFrame=" + mDisplayFrame);
+ pw.println(newPrefix + "mDisplayCutout=" + mDisplayCutout.get());
+ pw.println(newPrefix + "mRoundedCorners=" + mRoundedCorners);
for (int i = 0; i < SIZE; i++) {
InsetsSource source = mSources[i];
if (source == null) continue;
- source.dump(prefix + " ", pw);
+ source.dump(newPrefix + " ", pw);
}
}
@@ -698,7 +745,8 @@
InsetsState state = (InsetsState) o;
if (!mDisplayFrame.equals(state.mDisplayFrame)
- || !mDisplayCutout.equals(state.mDisplayCutout)) {
+ || !mDisplayCutout.equals(state.mDisplayCutout)
+ || !mRoundedCorners.equals(state.mRoundedCorners)) {
return false;
}
for (int i = 0; i < SIZE; i++) {
@@ -722,7 +770,8 @@
@Override
public int hashCode() {
- return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources));
+ return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources),
+ mRoundedCorners);
}
public InsetsState(Parcel in) {
@@ -739,6 +788,7 @@
mDisplayFrame.writeToParcel(dest, flags);
mDisplayCutout.writeToParcel(dest, flags);
dest.writeParcelableArray(mSources, 0);
+ dest.writeTypedObject(mRoundedCorners, flags);
}
public static final @android.annotation.NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
@@ -756,6 +806,7 @@
mDisplayFrame.set(Rect.CREATOR.createFromParcel(in));
mDisplayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
mSources = in.readParcelableArray(null, InsetsSource.class);
+ mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR);
}
@Override
@@ -770,6 +821,7 @@
return "InsetsState: {"
+ "mDisplayFrame=" + mDisplayFrame
+ ", mDisplayCutout=" + mDisplayCutout
+ + ", mRoundedCorners=" + mRoundedCorners
+ ", mSources= { " + joiner
+ " }";
}
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
new file mode 100644
index 0000000..cc7525b
--- /dev/null
+++ b/core/java/android/view/RoundedCorner.java
@@ -0,0 +1,239 @@
+/*
+ * 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.view;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents a rounded corner of the display.
+ *
+ * <code>
+ * ________
+ * / ^
+ * / | Radius
+ * | v
+ * | X <- Center point
+ * |<----->
+ * Radius
+ * </code>
+ *
+ * <p>Note: The rounded corner formed by the radius and the center is an approximation.</p>
+ *
+ * <p>{@link RoundedCorner} is immutable.</p>
+ */
+public final class RoundedCorner implements Parcelable {
+
+ /**
+ * The rounded corner is at the top-left of the screen.
+ */
+ public static final int POSITION_TOP_LEFT = 0;
+ /**
+ * The rounded corner is at the top-right of the screen.
+ */
+ public static final int POSITION_TOP_RIGHT = 1;
+ /**
+ * The rounded corner is at the bottom-right of the screen.
+ */
+ public static final int POSITION_BOTTOM_RIGHT = 2;
+ /**
+ * The rounded corner is at the bottom-left of the screen.
+ */
+ public static final int POSITION_BOTTOM_LEFT = 3;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "POSITION_" }, value = {
+ POSITION_TOP_LEFT,
+ POSITION_TOP_RIGHT,
+ POSITION_BOTTOM_RIGHT,
+ POSITION_BOTTOM_LEFT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Position {}
+
+ private final @Position int mPosition;
+ private final int mRadius;
+ @NonNull
+ private final Point mCenter;
+
+ /**
+ * Creates an empty {@link RoundedCorner} on the given position.
+ * @hide
+ */
+ @VisibleForTesting
+ public RoundedCorner(@Position int position) {
+ mPosition = position;
+ mRadius = 0;
+ mCenter = new Point(0, 0);
+ }
+
+ /**
+ * Creates a {@link RoundedCorner}.
+ *
+ * <p>Note that this is only useful for tests. For production code, developers should always
+ * use a {@link RoundedCorner} obtained from the system via
+ * {@link WindowInsets#getRoundedCorner} or {@link Display#getRoundedCorner}.</p>
+ *
+ * @param position the position of the rounded corner.
+ * @param radius the radius of the rounded corner.
+ * @param centerX the x of center point of the rounded corner.
+ * @param centerY the y of center point of the rounded corner.
+ *
+ */
+ public RoundedCorner(@Position int position, int radius, int centerX,
+ int centerY) {
+ mPosition = position;
+ mRadius = radius;
+ mCenter = new Point(centerX, centerY);
+ }
+
+ /**
+ * Creates a {@link RoundedCorner} from a passed in {@link RoundedCorner}.
+ *
+ * @hide
+ */
+ RoundedCorner(RoundedCorner rc) {
+ mPosition = rc.getPosition();
+ mRadius = rc.getRadius();
+ mCenter = new Point(rc.getCenter());
+ }
+
+ /**
+ * Get the position of this {@link RoundedCorner}.
+ *
+ * @see #POSITION_TOP_LEFT
+ * @see #POSITION_TOP_RIGHT
+ * @see #POSITION_BOTTOM_RIGHT
+ * @see #POSITION_BOTTOM_LEFT
+ */
+ public @Position int getPosition() {
+ return mPosition;
+ }
+
+ /**
+ * Returns the radius of a quarter circle approximation of this {@link RoundedCorner}.
+ *
+ * @return the rounded corner radius of this {@link RoundedCorner}. Returns 0 if there is no
+ * rounded corner.
+ */
+ public int getRadius() {
+ return mRadius;
+ }
+
+ /**
+ * Returns the circle center of a quarter circle approximation of this {@link RoundedCorner}.
+ *
+ * @return the center point of this {@link RoundedCorner} in the application's coordinate.
+ */
+ @NonNull
+ public Point getCenter() {
+ return new Point(mCenter);
+ }
+
+ /**
+ * Checks whether this {@link RoundedCorner} exists and is inside the application's bounds.
+ *
+ * @return {@code false} if there is a rounded corner and is contained in the application's
+ * bounds. Otherwise return {@code true}.
+ *
+ * @hide
+ */
+ public boolean isEmpty() {
+ return mRadius == 0 || mCenter.x == 0 || mCenter.y == 0;
+ }
+
+ private String getPositionString(@Position int position) {
+ switch (position) {
+ case POSITION_TOP_LEFT:
+ return "TopLeft";
+ case POSITION_TOP_RIGHT:
+ return "TopRight";
+ case POSITION_BOTTOM_RIGHT:
+ return "BottomRight";
+ case POSITION_BOTTOM_LEFT:
+ return "BottomLeft";
+ default:
+ return "Invalid";
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof RoundedCorner) {
+ RoundedCorner r = (RoundedCorner) o;
+ return mPosition == r.mPosition && mRadius == r.mRadius
+ && mCenter.equals(r.mCenter);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ result = 31 * result + mPosition;
+ result = 31 * result + mRadius;
+ result = 31 * result + mCenter.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "RoundedCorner{"
+ + "position=" + getPositionString(mPosition)
+ + ", radius=" + mRadius
+ + ", center=" + mCenter
+ + '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mPosition);
+ out.writeInt(mRadius);
+ out.writeInt(mCenter.x);
+ out.writeInt(mCenter.y);
+ }
+
+ public static final @NonNull Creator<RoundedCorner> CREATOR = new Creator<RoundedCorner>() {
+ @Override
+ public RoundedCorner createFromParcel(Parcel in) {
+ return new RoundedCorner(in.readInt(), in.readInt(), in.readInt(), in.readInt());
+ }
+
+ @Override
+ public RoundedCorner[] newArray(int size) {
+ return new RoundedCorner[size];
+ }
+ };
+}
diff --git a/core/java/android/net/VpnInfo.aidl b/core/java/android/view/RoundedCorners.aidl
similarity index 76%
copy from core/java/android/net/VpnInfo.aidl
copy to core/java/android/view/RoundedCorners.aidl
index 8bcaa81..0a901c0 100644
--- a/core/java/android/net/VpnInfo.aidl
+++ b/core/java/android/view/RoundedCorners.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
+/**
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net;
+package android.view;
-parcelable VpnInfo;
+parcelable RoundedCorners;
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
new file mode 100644
index 0000000..015e804
--- /dev/null
+++ b/core/java/android/view/RoundedCorners.java
@@ -0,0 +1,380 @@
+/*
+ * 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.view;
+
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+import android.view.RoundedCorner.Position;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+
+/**
+ * A class to create & manage all the {@link RoundedCorner} on the display.
+ *
+ * @hide
+ */
+public class RoundedCorners implements Parcelable {
+
+ public static final RoundedCorners NO_ROUNDED_CORNERS = new RoundedCorners(
+ new RoundedCorner(POSITION_TOP_LEFT), new RoundedCorner(POSITION_TOP_RIGHT),
+ new RoundedCorner(POSITION_BOTTOM_RIGHT), new RoundedCorner(POSITION_BOTTOM_LEFT));
+
+ /**
+ * The number of possible positions at which rounded corners can be located.
+ */
+ public static final int ROUNDED_CORNER_POSITION_LENGTH = 4;
+
+ private static final Object CACHE_LOCK = new Object();
+
+ @GuardedBy("CACHE_LOCK")
+ private static int sCachedDisplayWidth;
+ @GuardedBy("CACHE_LOCK")
+ private static int sCachedDisplayHeight;
+ @GuardedBy("CACHE_LOCK")
+ private static Pair<Integer, Integer> sCachedRadii;
+ @GuardedBy("CACHE_LOCK")
+ private static RoundedCorners sCachedRoundedCorners;
+
+ @VisibleForTesting
+ public final RoundedCorner[] mRoundedCorners;
+
+ public RoundedCorners(RoundedCorner[] roundedCorners) {
+ mRoundedCorners = roundedCorners;
+ }
+
+ public RoundedCorners(RoundedCorner topLeft, RoundedCorner topRight, RoundedCorner bottomRight,
+ RoundedCorner bottomLeft) {
+ mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ mRoundedCorners[POSITION_TOP_LEFT] = topLeft;
+ mRoundedCorners[POSITION_TOP_RIGHT] = topRight;
+ mRoundedCorners[POSITION_BOTTOM_RIGHT] = bottomRight;
+ mRoundedCorners[POSITION_BOTTOM_LEFT] = bottomLeft;
+ }
+
+ public RoundedCorners(RoundedCorners roundedCorners) {
+ mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
+ mRoundedCorners[i] = new RoundedCorner(roundedCorners.mRoundedCorners[i]);
+ }
+ }
+
+ /**
+ * Creates the rounded corners according to @android:dimen/rounded_corner_radius,
+ * @android:dimen/rounded_corner_radius_top and @android:dimen/rounded_corner_radius_bottom
+ */
+ public static RoundedCorners fromResources(
+ Resources res, int displayWidth, int displayHeight) {
+ return fromRadii(loadRoundedCornerRadii(res), displayWidth, displayHeight);
+ }
+
+ /**
+ * Creates the rounded corners from radius
+ */
+ @VisibleForTesting
+ public static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int displayWidth,
+ int displayHeight) {
+ if (radii == null) {
+ return null;
+ }
+
+ synchronized (CACHE_LOCK) {
+ if (radii.equals(sCachedRadii) && sCachedDisplayWidth == displayWidth
+ && sCachedDisplayHeight == displayHeight) {
+ return sCachedRoundedCorners;
+ }
+ }
+
+ final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ final int topRadius = radii.first > 0 ? radii.first : 0;
+ final int bottomRadius = radii.second > 0 ? radii.second : 0;
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
+ roundedCorners[i] = createRoundedCorner(
+ i,
+ i <= POSITION_TOP_RIGHT ? topRadius : bottomRadius,
+ displayWidth,
+ displayHeight);
+ }
+
+ final RoundedCorners result = new RoundedCorners(roundedCorners);
+ synchronized (CACHE_LOCK) {
+ sCachedDisplayWidth = displayWidth;
+ sCachedDisplayHeight = displayHeight;
+ sCachedRadii = radii;
+ sCachedRoundedCorners = result;
+ }
+ return result;
+ }
+
+ /**
+ * Loads the rounded corner radii from resources.
+ *
+ * @param res
+ * @return a Pair of radius. The first is the top rounded corner radius and second is the
+ * bottom corner radius.
+ */
+ @Nullable
+ private static Pair<Integer, Integer> loadRoundedCornerRadii(Resources res) {
+ final int radiusDefault = res.getDimensionPixelSize(R.dimen.rounded_corner_radius);
+ final int radiusTop = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_top);
+ final int radiusBottom = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_bottom);
+ if (radiusDefault == 0 && radiusTop == 0 && radiusBottom == 0) {
+ return null;
+ }
+ final Pair<Integer, Integer> radii = new Pair<>(
+ radiusTop > 0 ? radiusTop : radiusDefault,
+ radiusBottom > 0 ? radiusBottom : radiusDefault);
+ return radii;
+ }
+
+ /**
+ * Insets the reference frame of the rounded corners.
+ *
+ * @return a copy of this instance which has been inset
+ */
+ public RoundedCorners inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
+ final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
+ roundedCorners[i] = insetRoundedCorner(i, insetLeft, insetTop, insetRight, insetBottom);
+ }
+ return new RoundedCorners(roundedCorners);
+ }
+
+ private RoundedCorner insetRoundedCorner(@Position int position, int insetLeft,
+ int insetTop, int insetRight, int insetBottom) {
+ if (mRoundedCorners[position].isEmpty()) {
+ return new RoundedCorner(position);
+ }
+
+ final int radius = mRoundedCorners[position].getRadius();
+ final Point center = mRoundedCorners[position].getCenter();
+ boolean hasRoundedCorner;
+ switch (position) {
+ case POSITION_TOP_LEFT:
+ hasRoundedCorner = radius > insetTop || radius > insetLeft;
+ break;
+ case POSITION_TOP_RIGHT:
+ hasRoundedCorner = radius > insetTop || radius > insetRight;
+ break;
+ case POSITION_BOTTOM_RIGHT:
+ hasRoundedCorner = radius > insetBottom || radius > insetRight;
+ break;
+ case POSITION_BOTTOM_LEFT:
+ hasRoundedCorner = radius > insetBottom || radius > insetLeft;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "The position is not one of the RoundedCornerPosition =" + position);
+ }
+ return new RoundedCorner(
+ position, radius,
+ hasRoundedCorner ? center.x - insetLeft : 0,
+ hasRoundedCorner ? center.y - insetTop : 0);
+ }
+
+ /**
+ * Returns the {@link RoundedCorner} of the given position if there is one.
+ *
+ * @param position the position of the rounded corner on the display.
+ * @return the rounded corner of the given position. Returns {@code null} if
+ * {@link RoundedCorner#isEmpty()} is {@code true}.
+ */
+ @Nullable
+ public RoundedCorner getRoundedCorner(@Position int position) {
+ return mRoundedCorners[position].isEmpty()
+ ? null : new RoundedCorner(mRoundedCorners[position]);
+ }
+
+ /**
+ * Sets the rounded corner of given position.
+ *
+ * @param position the position of this rounded corner
+ * @param roundedCorner the rounded corner or null if there is none
+ */
+ public void setRoundedCorner(@Position int position, @Nullable RoundedCorner roundedCorner) {
+ mRoundedCorners[position] = roundedCorner == null
+ ? new RoundedCorner(position) : roundedCorner;
+ }
+
+ /**
+ * Returns an array of {@link RoundedCorner}s. Ordinal value of RoundedCornerPosition is used
+ * as an index of the array.
+ *
+ * @return an array of {@link RoundedCorner}s, one for each rounded corner area.
+ */
+ public RoundedCorner[] getAllRoundedCorners() {
+ RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
+ roundedCorners[i] = new RoundedCorner(roundedCorners[i]);
+ }
+ return roundedCorners;
+ }
+
+ /**
+ * Returns a scaled RoundedCorners.
+ */
+ public RoundedCorners scale(float scale) {
+ if (scale == 1f) {
+ return this;
+ }
+
+ RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
+ final RoundedCorner roundedCorner = mRoundedCorners[i];
+ roundedCorners[i] = new RoundedCorner(
+ i,
+ (int) (roundedCorner.getRadius() * scale),
+ (int) (roundedCorner.getCenter().x * scale),
+ (int) (roundedCorner.getCenter().y * scale));
+ }
+ return new RoundedCorners(roundedCorners);
+ }
+
+ /**
+ * Returns a rotated RoundedCorners.
+ */
+ public RoundedCorners rotate(@Surface.Rotation int rotation, int initialDisplayWidth,
+ int initialDisplayHeight) {
+ if (rotation == ROTATION_0) {
+ return this;
+ }
+ final boolean isSizeFlipped = rotation == ROTATION_90 || rotation == ROTATION_270;
+ RoundedCorner[] newCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ int newPosistion;
+ for (int i = 0; i < mRoundedCorners.length; i++) {
+ newPosistion = getRotatedIndex(i, rotation);
+ newCorners[newPosistion] = createRoundedCorner(
+ newPosistion,
+ mRoundedCorners[i].getRadius(),
+ isSizeFlipped ? initialDisplayHeight : initialDisplayWidth,
+ isSizeFlipped ? initialDisplayWidth : initialDisplayHeight);
+ }
+ return new RoundedCorners(newCorners);
+ }
+
+ private static RoundedCorner createRoundedCorner(@Position int position,
+ int radius, int displayWidth, int displayHeight) {
+ switch (position) {
+ case POSITION_TOP_LEFT:
+ return new RoundedCorner(
+ POSITION_TOP_LEFT,
+ radius,
+ radius > 0 ? radius : 0,
+ radius > 0 ? radius : 0);
+ case POSITION_TOP_RIGHT:
+ return new RoundedCorner(
+ POSITION_TOP_RIGHT,
+ radius,
+ radius > 0 ? displayWidth - radius : 0,
+ radius > 0 ? radius : 0);
+ case POSITION_BOTTOM_RIGHT:
+ return new RoundedCorner(
+ POSITION_BOTTOM_RIGHT,
+ radius,
+ radius > 0 ? displayWidth - radius : 0,
+ radius > 0 ? displayHeight - radius : 0);
+ case POSITION_BOTTOM_LEFT:
+ return new RoundedCorner(
+ POSITION_BOTTOM_LEFT,
+ radius,
+ radius > 0 ? radius : 0,
+ radius > 0 ? displayHeight - radius : 0);
+ default:
+ throw new IllegalArgumentException(
+ "The position is not one of the RoundedCornerPosition =" + position);
+ }
+ }
+
+ private static int getRotatedIndex(int position, int rotation) {
+ return (position - rotation + ROUNDED_CORNER_POSITION_LENGTH) % 4;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ for (RoundedCorner roundedCorner : mRoundedCorners) {
+ result = result * 31 + roundedCorner.hashCode();
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof RoundedCorners) {
+ RoundedCorners r = (RoundedCorners) o;
+ return Arrays.deepEquals(mRoundedCorners, ((RoundedCorners) o).mRoundedCorners);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "RoundedCorners{" + Arrays.toString(mRoundedCorners) + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (equals(NO_ROUNDED_CORNERS)) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ dest.writeTypedArray(mRoundedCorners, flags);
+ }
+ }
+
+ public static final @NonNull Creator<RoundedCorners> CREATOR = new Creator<RoundedCorners>() {
+ @Override
+ public RoundedCorners createFromParcel(Parcel in) {
+ int variant = in.readInt();
+ if (variant == 0) {
+ return NO_ROUNDED_CORNERS;
+ }
+ RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ in.readTypedArray(roundedCorners, RoundedCorner.CREATOR);
+ return new RoundedCorners(roundedCorners);
+ }
+
+ @Override
+ public RoundedCorners[] newArray(int size) {
+ return new RoundedCorners[size];
+ }
+ };
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0a1a231..acd2507 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -750,18 +750,22 @@
private abstract static class CaptureArgs {
private final int mPixelFormat;
private final Rect mSourceCrop = new Rect();
- private final float mFrameScale;
+ private final float mFrameScaleX;
+ private final float mFrameScaleY;
private final boolean mCaptureSecureLayers;
private final boolean mAllowProtected;
private final long mUid;
+ private final boolean mGrayscale;
private CaptureArgs(Builder<? extends Builder<?>> builder) {
mPixelFormat = builder.mPixelFormat;
mSourceCrop.set(builder.mSourceCrop);
- mFrameScale = builder.mFrameScale;
+ mFrameScaleX = builder.mFrameScaleX;
+ mFrameScaleY = builder.mFrameScaleY;
mCaptureSecureLayers = builder.mCaptureSecureLayers;
mAllowProtected = builder.mAllowProtected;
mUid = builder.mUid;
+ mGrayscale = builder.mGrayscale;
}
/**
@@ -772,10 +776,12 @@
abstract static class Builder<T extends Builder<T>> {
private int mPixelFormat = PixelFormat.RGBA_8888;
private final Rect mSourceCrop = new Rect();
- private float mFrameScale = 1;
+ private float mFrameScaleX = 1;
+ private float mFrameScaleY = 1;
private boolean mCaptureSecureLayers;
private boolean mAllowProtected;
private long mUid = -1;
+ private boolean mGrayscale;
/**
* The desired pixel format of the returned buffer.
@@ -798,7 +804,18 @@
* The desired scale of the returned buffer. The raw screen will be scaled up/down.
*/
public T setFrameScale(float frameScale) {
- mFrameScale = frameScale;
+ mFrameScaleX = frameScale;
+ mFrameScaleY = frameScale;
+ return getThis();
+ }
+
+ /**
+ * The desired scale of the returned buffer, allowing separate values for x and y scale.
+ * The raw screen will be scaled up/down.
+ */
+ public T setFrameScale(float frameScaleX, float frameScaleY) {
+ mFrameScaleX = frameScaleX;
+ mFrameScaleY = frameScaleY;
return getThis();
}
@@ -834,6 +851,14 @@
}
/**
+ * Set whether the screenshot should use grayscale or not.
+ */
+ public T setGrayscale(boolean grayscale) {
+ mGrayscale = grayscale;
+ return getThis();
+ }
+
+ /**
* Each sub class should return itself to allow the builder to chain properly
*/
abstract T getThis();
@@ -929,7 +954,7 @@
/**
* The arguments class used to make layer capture requests.
*
- * @see #nativeCaptureLayers(LayerCaptureArgs)
+ * @see #nativeCaptureLayers(LayerCaptureArgs, ScreenCaptureListener)
* @hide
*/
public static class LayerCaptureArgs extends CaptureArgs {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f603ef7..70ec2d4 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -877,6 +877,10 @@
synchronized (mSurfaceControlLock) {
mSurface.release();
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
+ mBlastBufferQueue = null;
+ }
if (mRtHandlingPositionUpdates) {
mRtReleaseSurfaces = true;
@@ -901,10 +905,6 @@
transaction.remove(mBlastSurfaceControl);
mBlastSurfaceControl = null;
}
- if (mBlastBufferQueue != null) {
- mBlastBufferQueue.destroy();
- mBlastBufferQueue = null;
- }
}
private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index acbcbfa..b10370a 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -85,13 +85,12 @@
for (int i = params.length - 1; i >= 0; i--) {
SurfaceParams surfaceParams = params[i];
SurfaceControl surface = surfaceParams.surface;
+ if (frame > 0) {
+ t.deferTransactionUntil(surface, mTargetSc, frame);
+ }
applyParams(t, surfaceParams, mTmpFloat9);
}
- if (mTargetViewRootImpl != null) {
- mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
- } else {
- t.apply();
- }
+ t.apply();
}
public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5c0e156..749c0df 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -41,6 +41,7 @@
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.StyleRes;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.annotation.UiContext;
import android.annotation.UiThread;
@@ -9030,7 +9031,8 @@
* not be null or empty if a non-null listener is passed in.
* @param listener The listener to use. This can be null to reset to the default behavior.
*/
- public void setOnReceiveContentListener(@Nullable String[] mimeTypes,
+ public void setOnReceiveContentListener(
+ @SuppressLint("NullableCollection") @Nullable String[] mimeTypes,
@Nullable OnReceiveContentListener listener) {
if (listener != null) {
Preconditions.checkArgument(mimeTypes != null && mimeTypes.length > 0,
@@ -9106,6 +9108,7 @@
* @return The MIME types accepted by {@link #performReceiveContent} for this view (may
* include patterns such as "image/*").
*/
+ @SuppressLint("NullableCollection")
@Nullable
public String[] getOnReceiveContentMimeTypes() {
return mOnReceiveContentMimeTypes;
@@ -9803,20 +9806,6 @@
}
}
- private void notifyAttachForDrawables() {
- if (mBackground != null) mBackground.onAttached(this);
- if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
- mForegroundInfo.mDrawable.onAttached(this);
- }
- }
-
- private void notifyDetachForDrawables() {
- if (mBackground != null) mBackground.onDetached(this);
- if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
- mForegroundInfo.mDrawable.onDetached(this);
- }
- }
-
private void setNotifiedContentCaptureAppeared() {
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
@@ -10364,6 +10353,7 @@
for (int i = 0; i < childDrawIndex; i++) {
drawingOrderInParent += numViewsForAccessibility(preorderedList.get(i));
}
+ preorderedList.clear();
} else {
final int childIndex = parentGroup.indexOfChild(viewAtDrawingLevel);
final boolean customOrder = parentGroup.isChildrenDrawingOrderEnabled();
@@ -20668,7 +20658,6 @@
notifyEnterOrExitForAutoFillIfNeeded(true);
notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
- notifyAttachForDrawables();
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -20718,7 +20707,6 @@
notifyEnterOrExitForAutoFillIfNeeded(false);
notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
- notifyDetachForDrawables();
}
/**
@@ -21367,6 +21355,10 @@
int height = mBottom - mTop;
int layerType = getLayerType();
+ // Hacky hack: Reset any stretch effects as those are applied during the draw pass
+ // instead of being "stateful" like other RenderNode properties
+ renderNode.clearStretch();
+
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
try {
@@ -22793,6 +22785,11 @@
final Rect bounds = drawable.getBounds();
final int width = bounds.width();
final int height = bounds.height();
+
+ // Hacky hack: Reset any stretch effects as those are applied during the draw pass
+ // instead of being "stateful" like other RenderNode properties
+ renderNode.clearStretch();
+
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
// Reverse left/top translation done by drawable canvas, which will
@@ -23847,7 +23844,6 @@
if (mBackground != null) {
if (isAttachedToWindow()) {
mBackground.setVisible(false, false);
- mBackground.onDetached(this);
}
mBackground.setCallback(null);
unscheduleDrawable(mBackground);
@@ -23897,7 +23893,6 @@
background.setState(getDrawableState());
}
if (isAttachedToWindow()) {
- background.onAttached(this);
background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
@@ -24130,7 +24125,6 @@
if (mForegroundInfo.mDrawable != null) {
if (isAttachedToWindow()) {
mForegroundInfo.mDrawable.setVisible(false, false);
- mForegroundInfo.mDrawable.onDetached(this);
}
mForegroundInfo.mDrawable.setCallback(null);
unscheduleDrawable(mForegroundInfo.mDrawable);
@@ -24148,7 +24142,6 @@
}
applyForegroundTint();
if (isAttachedToWindow()) {
- foreground.onAttached(this);
foreground.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
// Set callback last, since the view may still be initializing.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 52d0062..18ef80c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -115,7 +115,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RenderNode;
-import android.graphics.drawable.BackgroundBlurDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.hardware.display.DisplayManager;
@@ -139,6 +138,7 @@
import android.os.UserHandle;
import android.sysprop.DisplayProperties;
import android.util.AndroidRuntimeException;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -158,6 +158,7 @@
import android.view.View.FocusDirection;
import android.view.View.MeasureSpec;
import android.view.Window.OnContentApplyWindowInsetsListener;
+import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -187,6 +188,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
@@ -672,14 +674,6 @@
new BackgroundBlurDrawable.Aggregator(this);
/**
- * @return {@link BackgroundBlurDrawable.Aggregator} for this instance.
- */
- @NonNull
- public BackgroundBlurDrawable.Aggregator getBlurRegionAggregator() {
- return mBlurRegionAggregator;
- }
-
- /**
* @return {@link ImeFocusController} for this instance.
*/
@NonNull
@@ -934,6 +928,33 @@
}
}
+ // TODO(b/161810301): Make this private after window layout is moved to the client side.
+ public static void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
+ Rect displayFrame, Rect outBounds) {
+ final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
+ final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
+ final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
+ final Rect df = displayFrame;
+ Insets insets = Insets.of(0, 0, 0, 0);
+ for (int i = types.size() - 1; i >= 0; i--) {
+ final InsetsSource source = state.peekSource(types.valueAt(i));
+ if (source == null) {
+ continue;
+ }
+ insets = Insets.max(insets, source.calculateInsets(
+ df, attrs.isFitInsetsIgnoringVisibility()));
+ }
+ final int left = (sidesToFit & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
+ final int top = (sidesToFit & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
+ final int right = (sidesToFit & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
+ final int bottom = (sidesToFit & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
+ outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom);
+ }
+
+ private Configuration getConfiguration() {
+ return mContext.getResources().getConfiguration();
+ }
+
/**
* We have one child
*/
@@ -1065,18 +1086,15 @@
controlInsetsForCompatibility(mWindowAttributes);
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
- mInsetsController.getRequestedVisibility(), mTmpFrames.frame,
- inputChannel, mTempInsets, mTempControls);
+ mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
+ mTempControls);
if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
}
- setFrame(mTmpFrames.frame);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
- inputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
@@ -1092,6 +1110,9 @@
mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls);
+ computeWindowBounds(mWindowAttributes, mInsetsController.getState(),
+ getConfiguration().windowConfiguration.getBounds(), mTmpFrames.frame);
+ setFrame(mTmpFrames.frame);
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -1365,7 +1386,7 @@
}
private int getNightMode() {
- return mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
}
private void updateForceDarkMode() {
@@ -2341,7 +2362,7 @@
/* package */ WindowInsets getWindowInsets(boolean forceConstruct) {
if (mLastWindowInsets == null || forceConstruct) {
- final Configuration config = mContext.getResources().getConfiguration();
+ final Configuration config = getConfiguration();
mLastWindowInsets = mInsetsController.calculateInsets(
config.isScreenRound(), mAttachInfo.mAlwaysConsumeSystemBars,
mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
@@ -2477,7 +2498,7 @@
mFullRedrawNeeded = true;
mLayoutRequested = true;
- final Configuration config = mContext.getResources().getConfiguration();
+ final Configuration config = getConfiguration();
if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
@@ -4770,7 +4791,7 @@
}
// TODO: Centralize this sanitization? Why do we let setting bad modes?
// Alternatively, can we just let HWUI figure it out? Do we need to care here?
- if (!mContext.getResources().getConfiguration().isScreenWideColorGamut()) {
+ if (!getConfiguration().isScreenWideColorGamut()) {
colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
}
mAttachInfo.mThreadedRenderer.setColorMode(colorMode);
@@ -10084,6 +10105,13 @@
}
}
+ /**
+ * Creates a background blur drawable for the backing {@link Surface}.
+ */
+ public BackgroundBlurDrawable createBackgroundBlurDrawable() {
+ return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext);
+ }
+
@Override
public void onDescendantUnbufferedRequested() {
mUnbufferedInputSource = mView.mUnbufferedInputSource;
@@ -10109,7 +10137,7 @@
* Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures
* you can add transactions to the upcoming frame.
*/
- public void mergeWithNextTransaction(Transaction t, long frameNumber) {
+ void mergeWithNextTransaction(Transaction t, long frameNumber) {
if (mBlastBufferQueue != null) {
mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber);
}
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index f5aa97a..8b3fb2e 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
@@ -377,7 +378,8 @@
* <p>Should only be set when the node is used for Autofill or Content Capture purposes - it
* will be ignored when used for Assist.
*/
- public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) {}
+ public void setOnReceiveContentMimeTypes(
+ @SuppressLint("NullableCollection") @Nullable String[] mimeTypes) {}
/**
* Sets the {@link android.text.InputType} bits of this node.
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 7a5561c..41c38a1 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -82,6 +82,7 @@
@Nullable private Rect mTempRect;
private final boolean mIsRound;
@Nullable private final DisplayCutout mDisplayCutout;
+ @Nullable private final RoundedCorners mRoundedCorners;
/**
* In multi-window we force show the navigation bar. Because we don't want that the surface size
@@ -125,11 +126,12 @@
* @hide
* @deprecated Use {@link WindowInsets(SparseArray, SparseArray, boolean, boolean, DisplayCutout)}
*/
- public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect,
- boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
+ @Deprecated
+ public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect, boolean isRound,
+ boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)),
- isRound, alwaysConsumeSystemBars, displayCutout, systemBars(),
+ isRound, alwaysConsumeSystemBars, displayCutout, null, systemBars(),
false /* compatIgnoreVisibility */);
}
@@ -150,7 +152,8 @@
boolean[] typeVisibilityMap,
boolean isRound,
boolean alwaysConsumeSystemBars, DisplayCutout displayCutout,
- @InsetsType int compatInsetsTypes, boolean compatIgnoreVisibility) {
+ RoundedCorners roundedCorners, @InsetsType int compatInsetsTypes,
+ boolean compatIgnoreVisibility) {
mSystemWindowInsetsConsumed = typeInsetsMap == null;
mTypeInsetsMap = mSystemWindowInsetsConsumed
? new Insets[SIZE]
@@ -170,6 +173,8 @@
mDisplayCutoutConsumed = displayCutout == null;
mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty())
? null : displayCutout;
+
+ mRoundedCorners = roundedCorners;
}
/**
@@ -182,6 +187,7 @@
src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
src.mTypeVisibilityMap, src.mIsRound,
src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
+ src.mRoundedCorners,
src.mCompatInsetsTypes,
src.mCompatIgnoreVisibility);
}
@@ -235,7 +241,7 @@
@UnsupportedAppUsage
public WindowInsets(Rect systemWindowInsets) {
this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null,
- systemBars(), false /* compatIgnoreVisibility */);
+ null, systemBars(), false /* compatIgnoreVisibility */);
}
/**
@@ -466,7 +472,7 @@
public boolean hasInsets() {
return !getInsets(mTypeInsetsMap, all()).equals(Insets.NONE)
|| !getInsets(mTypeMaxInsetsMap, all()).equals(Insets.NONE)
- || mDisplayCutout != null;
+ || mDisplayCutout != null || mRoundedCorners != null;
}
/**
@@ -487,6 +493,23 @@
}
/**
+ * Returns the {@link RoundedCorner} of the given position if there is one.
+ *
+ * @param position the position of the rounded corner on the display. The value should be one of
+ * the following:
+ * {@link RoundedCorner#POSITION_TOP_LEFT},
+ * {@link RoundedCorner#POSITION_TOP_RIGHT},
+ * {@link RoundedCorner#POSITION_BOTTOM_RIGHT},
+ * {@link RoundedCorner#POSITION_BOTTOM_LEFT}.
+ * @return the rounded corner of the given position. Returns {@code null} if there is none or
+ * the rounded corner area is not inside the application's bounds.
+ */
+ @Nullable
+ public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
+ return mRoundedCorners == null ? null : mRoundedCorners.getRoundedCorner(position);
+ }
+
+ /**
* Returns a copy of this WindowInsets with the cutout fully consumed.
*
* @return A modified copy of this WindowInsets
@@ -501,7 +524,7 @@
mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
- null /* displayCutout */,
+ null /* displayCutout */, mRoundedCorners,
mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -553,7 +576,7 @@
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
displayCutoutCopyConstructorArgument(this),
- mCompatInsetsTypes, mCompatIgnoreVisibility);
+ mRoundedCorners, mCompatInsetsTypes, mCompatIgnoreVisibility);
}
// TODO(b/119190588): replace @code with @link below
@@ -856,6 +879,8 @@
result.append(mDisplayCutout != null ? "cutout=" + mDisplayCutout : "");
result.append("\n ");
+ result.append(mRoundedCorners != null ? "roundedCorners=" + mRoundedCorners : "");
+ result.append("\n ");
result.append(isRound() ? "round" : "");
result.append("}");
return result.toString();
@@ -947,6 +972,9 @@
: mDisplayCutout == null
? DisplayCutout.NO_CUTOUT
: mDisplayCutout.inset(left, top, right, bottom),
+ mRoundedCorners == null
+ ? RoundedCorners.NO_ROUNDED_CORNERS
+ : mRoundedCorners.inset(left, top, right, bottom),
mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -964,13 +992,14 @@
&& Arrays.equals(mTypeInsetsMap, that.mTypeInsetsMap)
&& Arrays.equals(mTypeMaxInsetsMap, that.mTypeMaxInsetsMap)
&& Arrays.equals(mTypeVisibilityMap, that.mTypeVisibilityMap)
- && Objects.equals(mDisplayCutout, that.mDisplayCutout);
+ && Objects.equals(mDisplayCutout, that.mDisplayCutout)
+ && Objects.equals(mRoundedCorners, that.mRoundedCorners);
}
@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
- Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout,
+ Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners,
mAlwaysConsumeSystemBars, mSystemWindowInsetsConsumed, mStableInsetsConsumed,
mDisplayCutoutConsumed);
}
@@ -1032,6 +1061,7 @@
private boolean mStableInsetsConsumed = true;
private DisplayCutout mDisplayCutout;
+ private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
private boolean mIsRound;
private boolean mAlwaysConsumeSystemBars;
@@ -1057,6 +1087,7 @@
mSystemInsetsConsumed = insets.mSystemWindowInsetsConsumed;
mStableInsetsConsumed = insets.mStableInsetsConsumed;
mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
+ mRoundedCorners = insets.mRoundedCorners;
mIsRound = insets.mIsRound;
mAlwaysConsumeSystemBars = insets.mAlwaysConsumeSystemBars;
}
@@ -1262,6 +1293,29 @@
/** @hide */
@NonNull
+ public Builder setRoundedCorners(RoundedCorners roundedCorners) {
+ mRoundedCorners = roundedCorners != null
+ ? roundedCorners : RoundedCorners.NO_ROUNDED_CORNERS;
+ return this;
+ }
+
+ /**
+ * Sets the rounded corner of given position.
+ *
+ * @see #getRoundedCorner(int)
+ * @param position the position of this rounded corner
+ * @param roundedCorner the rounded corner or null if there is none
+ * @return itself
+ */
+ @NonNull
+ public Builder setRoundedCorner(@RoundedCorner.Position int position,
+ @Nullable RoundedCorner roundedCorner) {
+ mRoundedCorners.setRoundedCorner(position, roundedCorner);
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
public Builder setRound(boolean round) {
mIsRound = round;
return this;
@@ -1283,7 +1337,7 @@
public WindowInsets build() {
return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout,
+ mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout, mRoundedCorners,
systemBars(), false /* compatIgnoreVisibility */);
}
}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 991ed55..227b9f4 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -67,13 +67,26 @@
int APPEARANCE_LIGHT_NAVIGATION_BARS = 1 << 4;
/**
+ * Makes status bars semi-transparent with dark background and light foreground.
+ * @hide
+ */
+ int APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS = 1 << 5;
+
+ /**
+ * Makes navigation bars semi-transparent with dark background and light foreground.
+ * @hide
+ */
+ int APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS = 1 << 6;
+
+ /**
* Determines the appearance of system bars.
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {APPEARANCE_OPAQUE_STATUS_BARS, APPEARANCE_OPAQUE_NAVIGATION_BARS,
APPEARANCE_LOW_PROFILE_BARS, APPEARANCE_LIGHT_STATUS_BARS,
- APPEARANCE_LIGHT_NAVIGATION_BARS})
+ APPEARANCE_LIGHT_NAVIGATION_BARS, APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS,
+ APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS})
@interface Appearance {
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 7faa222..9e87c95 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -83,6 +83,7 @@
import android.Manifest.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -99,6 +100,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Build;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1509,13 +1511,7 @@
* Use {@link #dimAmount} to control the amount of dim. */
public static final int FLAG_DIM_BEHIND = 0x00000002;
- /** Window flag: enable blurring behind this window.
- * To set the amount of blur, use {@link #backgroundBlurRadius}
- *
- * @hide
- */
- @RequiresPermission(permission.USE_BACKGROUND_BLUR)
- @SystemApi
+ /** Window flag: enable blur behind for this window. */
public static final int FLAG_BLUR_BEHIND = 0x00000004;
/** Window flag: this window won't ever get key input focus, so the
@@ -1555,17 +1551,26 @@
* <li><b>Fully transparent windows</b>: This window has {@link LayoutParams#alpha} equal
* to 0.
* <li><b>One SAW window with enough transparency</b>: This window is of type {@link
- * #TYPE_APPLICATION_OVERLAY}, has {@link LayoutParams#alpha} below or equal to <b>0.8</b>
- * and it's the <b>only</b> window of type {@link #TYPE_APPLICATION_OVERLAY} from this UID
- * in the touch path.
+ * #TYPE_APPLICATION_OVERLAY}, has {@link LayoutParams#alpha} below or equal to the
+ * <a href="#MaximumOpacity">maximum obscuring opacity</a> (see below) and it's the
+ * <b>only</b> window of type {@link #TYPE_APPLICATION_OVERLAY} from this UID in the touch
+ * path.
* <li><b>Multiple SAW windows with enough transparency</b>: The multiple overlapping
* {@link #TYPE_APPLICATION_OVERLAY} windows in the
* touch path from this UID have a <b>combined obscuring opacity</b> below or equal to
- * <b>0.8</b>. See section below on how to compute this value.
+ * the <a href="#MaximumOpacity">maximum obscuring opacity</a>. See section
+ * <a href="#ObscuringOpacity">Combined obscuring opacity</a> below on how to compute this
+ * value.
* </ol>
* <p>If none of these cases hold, the touch will not be delivered and a message will be
* logged to logcat.</p>
*
+ * <a name="MaximumOpacity"></a>
+ * <h3>Maximum obscuring opacity</h3>
+ * <p>This value is <b>0.8</b>. Apps that want to gather this value from the system rather
+ * than hard-coding it might want to use {@link
+ * android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch()}.</p>
+ *
* <a name="ObscuringOpacity"></a>
* <h3>Combined obscuring opacity</h3>
*
@@ -2846,12 +2851,14 @@
/**
* The token of {@link android.app.WindowContext}. It is usually a
- * {@link android.app.WindowTokenClient} and is used for updating
- * {@link android.content.res.Resources} from {@link Configuration} propagated from the
- * server side.
+ * {@link android.app.WindowTokenClient} and is used for associating the params with an
+ * existing node in the WindowManager hierarchy and getting the corresponding
+ * {@link Configuration} and {@link android.content.res.Resources} values with updates
+ * propagated from the server side.
*
* @hide
*/
+ @Nullable
public IBinder mWindowContextToken = null;
/**
@@ -3224,15 +3231,16 @@
public boolean preferMinimalPostProcessing = false;
/**
- * When {@link FLAG_BLUR_BEHIND} is set, this is the amount of blur in pixels that this
- * window will use to blur behind itself.
- * The range is from 0, which means no blur, to 150.
+ * Specifies the amount of blur to be used to blur everything behind the window.
+ * The effect is similar to the dimAmount, but instead of dimming, the content behind
+ * will be blurred.
*
- * @hide
+ * The blur behind radius range starts at 0, which means no blur, and increases until 150
+ * for the densest blur.
+ *
+ * @see #FLAG_BLUR_BEHIND
*/
- @SystemApi
- @RequiresPermission(permission.USE_BACKGROUND_BLUR)
- public int backgroundBlurRadius = 0;
+ public int blurBehindRadius = 0;
/**
* The color mode requested by this window. The target display may
@@ -3538,6 +3546,37 @@
return userActivityTimeout;
}
+ /**
+ * Sets the {@link android.app.WindowContext} token.
+ *
+ * @see #getWindowContextToken()
+ *
+ * @hide
+ */
+ @TestApi
+ public final void setWindowContextToken(@NonNull IBinder token) {
+ mWindowContextToken = token;
+ }
+
+ /**
+ * Gets the {@link android.app.WindowContext} token.
+ *
+ * The token is usually a {@link android.app.WindowTokenClient} and is used for associating
+ * the params with an existing node in the WindowManager hierarchy and getting the
+ * corresponding {@link Configuration} and {@link android.content.res.Resources} values with
+ * updates propagated from the server side.
+ *
+ * @see android.app.WindowTokenClient
+ * @see Context#createWindowContext(Display, int, Bundle)
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ public final IBinder getWindowContextToken() {
+ return mWindowContextToken;
+ }
+
public int describeContents() {
return 0;
}
@@ -3590,7 +3629,7 @@
out.writeInt(mFitInsetsSides);
out.writeBoolean(mFitInsetsIgnoringVisibility);
out.writeBoolean(preferMinimalPostProcessing);
- out.writeInt(backgroundBlurRadius);
+ out.writeInt(blurBehindRadius);
if (providesInsetsTypes != null) {
out.writeInt(providesInsetsTypes.length);
out.writeIntArray(providesInsetsTypes);
@@ -3659,7 +3698,7 @@
mFitInsetsSides = in.readInt();
mFitInsetsIgnoringVisibility = in.readBoolean();
preferMinimalPostProcessing = in.readBoolean();
- backgroundBlurRadius = in.readInt();
+ blurBehindRadius = in.readInt();
int insetsTypesLength = in.readInt();
if (insetsTypesLength > 0) {
providesInsetsTypes = new int[insetsTypesLength];
@@ -3904,8 +3943,8 @@
changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED;
}
- if (backgroundBlurRadius != o.backgroundBlurRadius) {
- backgroundBlurRadius = o.backgroundBlurRadius;
+ if (blurBehindRadius != o.blurBehindRadius) {
+ blurBehindRadius = o.blurBehindRadius;
changes |= BACKGROUND_BLUR_RADIUS_CHANGED;
}
@@ -4072,9 +4111,9 @@
sb.append(" preferMinimalPostProcessing=");
sb.append(preferMinimalPostProcessing);
}
- if (backgroundBlurRadius != 0) {
- sb.append(" backgroundBlurRadius=");
- sb.append(backgroundBlurRadius);
+ if (blurBehindRadius != 0) {
+ sb.append(" blurBehindRadius=");
+ sb.append(blurBehindRadius);
}
sb.append(System.lineSeparator());
sb.append(prefix).append(" fl=").append(
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index dd56c15..b85f1079 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -24,7 +24,7 @@
import android.graphics.Region;
import android.os.IBinder;
import android.os.RemoteException;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.util.Log;
import android.util.MergedConfiguration;
import android.window.ClientWindowFrames;
@@ -135,7 +135,7 @@
*/
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
+ int viewVisibility, int displayId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
@@ -171,10 +171,10 @@
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
- Rect outFrame, InputChannel outInputChannel, InsetsState outInsetsState,
+ InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibility,
- outFrame, outInputChannel, outInsetsState, outActiveControls);
+ outInputChannel, outInsetsState, outActiveControls);
}
@Override
@@ -466,7 +466,7 @@
}
@Override
- public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+ public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
String hashAlgorithm) {
return null;
}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 73962d7..10fd0e0 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -82,6 +82,7 @@
public static InlineSuggestionInfo newInlineSuggestionInfo(
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
+ @SuppressLint("NullableCollection")
@Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned) {
return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index a76d46d1..f3111bd 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -858,7 +858,7 @@
boolean reportFullscreenMode(boolean enabled);
/**
- * Have the editor perform spell checking around the current selection.
+ * Have the editor perform spell checking for the full content.
*
* <p>The editor can ignore this method call if it does not support spell checking.
*
diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/view/inputmethod/OWNERS
+++ b/core/java/android/view/inputmethod/OWNERS
@@ -2,3 +2,5 @@
set noparent
include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 0a1aea3..5980cb6 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -187,7 +187,8 @@
* @return The spell checker session of the spell checker.
*/
@Nullable
- public SpellCheckerSession newSpellCheckerSession(@Nullable Bundle bundle,
+ public SpellCheckerSession newSpellCheckerSession(
+ @SuppressLint("NullableCollection") @Nullable Bundle bundle,
@SuppressLint("UseIcu") @Nullable Locale locale,
@NonNull SpellCheckerSessionListener listener,
@SuppressLint("ListenerLast") boolean referToSpellCheckerLanguageSettings,
@@ -277,6 +278,7 @@
* @return The list of currently enabled spell checkers.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public List<SpellCheckerInfo> getEnabledSpellCheckersList() {
final SpellCheckerInfo[] enabledSpellCheckers = getEnabledSpellCheckers();
return enabledSpellCheckers != null ? Arrays.asList(enabledSpellCheckers) : null;
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index fa46146..b49d3c0 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -101,15 +101,17 @@
}
break;
case STATE_UI_TRANSLATION_PAUSED:
- runForEachView((view) -> view.onPauseUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+ runForEachView(View::onPauseUiTranslation);
break;
case STATE_UI_TRANSLATION_RESUMED:
- runForEachView((view) -> view.onRestoreUiTranslation(),
- STATE_UI_TRANSLATION_PAUSED);
+ runForEachView(View::onRestoreUiTranslation);
break;
case STATE_UI_TRANSLATION_FINISHED:
destroyTranslators();
- runForEachView((view) -> view.onFinishUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+ runForEachView(View::onFinishUiTranslation);
+ synchronized (mLock) {
+ mViews.clear();
+ }
break;
default:
Log.w(TAG, "onAutoTranslationStateChange(): unknown state: " + state);
@@ -191,9 +193,6 @@
*/
private void onUiTranslationStarted(Translator translator, List<AutofillId> views) {
synchronized (mLock) {
- if (views == null || views.size() == 0) {
- throw new IllegalArgumentException("Invalid empty views: " + views);
- }
// Find Views collect the translation data
// TODO(b/178084101): try to optimize, e.g. to this in a single traversal
final int viewCounts = views.size();
@@ -223,22 +222,18 @@
}
}
- private void runForEachView(Consumer<View> action, @UiTranslationState int state) {
+ private void runForEachView(Consumer<View> action) {
synchronized (mLock) {
+ final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews);
mActivity.runOnUiThread(() -> {
- final int viewCounts = mViews.size();
+ final int viewCounts = views.size();
for (int i = 0; i < viewCounts; i++) {
- final View view = mViews.valueAt(i).get();
+ final View view = views.valueAt(i).get();
if (view == null) {
- Log.w(TAG, "The View for autofill id " + mViews.keyAt(i)
- + " may be gone for state " + stateToString(state));
continue;
}
action.accept(view);
}
- if (state == STATE_UI_TRANSLATION_FINISHED) {
- mViews.clear();
- }
});
}
}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 023d9ff2..20230e7 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -98,9 +98,17 @@
public abstract boolean acceptThirdPartyCookies(WebView webview);
/**
- * Sets a cookie for the given URL. Any existing cookie with the same host,
- * path and name will be replaced with the new cookie. The cookie being set
- * will be ignored if it is expired.
+ * Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same
+ * host, path and name will be replaced with the new cookie. The cookie being set
+ * will be ignored if it is expired. To set multiple cookies, your application should invoke
+ * this method multiple times.
+ *
+ * <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP
+ * response header defined by
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>.
+ * This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of
+ * cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please
+ * consult the RFC specification for a list of valid attributes.
*
* <p class="note"><b>Note:</b> if specifying a {@code value} containing the {@code "Secure"}
* attribute, {@code url} must use the {@code "https://"} scheme.
@@ -112,13 +120,20 @@
public abstract void setCookie(String url, String value);
/**
- * Sets a cookie for the given URL. Any existing cookie with the same host,
- * path and name will be replaced with the new cookie. The cookie being set
- * will be ignored if it is expired.
- * <p>
- * This method is asynchronous.
- * If a {@link ValueCallback} is provided,
- * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
+ * Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same
+ * host, path and name will be replaced with the new cookie. The cookie being set
+ * will be ignored if it is expired. To set multiple cookies, your application should invoke
+ * this method multiple times.
+ *
+ * <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP
+ * response header defined by
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>.
+ * This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of
+ * cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please
+ * consult the RFC specification for a list of valid attributes.
+ *
+ * <p>This method is asynchronous. If a {@link ValueCallback} is provided,
+ * {@link ValueCallback#onReceiveValue} will be called on the current
* thread's {@link android.os.Looper} once the operation is complete.
* The value provided to the callback indicates whether the cookie was set successfully.
* You can pass {@code null} as the callback if you don't need to know when the operation
@@ -137,7 +152,10 @@
callback);
/**
- * Gets the cookies for the given URL.
+ * Gets all the cookies for the given URL. This may return multiple key-value pairs if multiple
+ * cookies are associated with this URL, in which case each cookie will be delimited by {@code
+ * "; "} characters (semicolon followed by a space). Each key-value pair will be of the form
+ * {@code "key=value"}.
*
* @param url the URL for which the cookies are requested
* @return value the cookies as a string, using the format of the 'Cookie'
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 950dc73..c7eac6c 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -218,4 +218,15 @@
public String getDataDirectorySuffix() {
return WebViewFactory.getDataDirectorySuffix();
}
+
+ /**
+ * Returns an array of startup timestamps. For the specification of array
+ * see {@link WebViewFactory.Timestamp}.
+ * This method must be called on the same thread where the
+ * WebViewChromiumFactoryProvider#create method was invoked.
+ */
+ @NonNull
+ public long[] getTimestamps() {
+ return WebViewFactory.getTimestamps();
+ }
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index b91e7d3..2e75834 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -35,6 +37,8 @@
import android.util.Log;
import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
/**
@@ -67,6 +71,33 @@
private static boolean sWebViewDisabled;
private static String sDataDirectorySuffix; // stored here so it can be set without loading WV
+ // Indices in sTimestamps array.
+ /** @hide */
+ @IntDef(value = {
+ WEBVIEW_LOAD_START, CREATE_CONTEXT_START, CREATE_CONTEXT_END,
+ ADD_ASSETS_START, ADD_ASSETS_END, GET_CLASS_LOADER_START, GET_CLASS_LOADER_END,
+ NATIVE_LOAD_START, NATIVE_LOAD_END,
+ PROVIDER_CLASS_FOR_NAME_START, PROVIDER_CLASS_FOR_NAME_END})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Timestamp {
+ }
+
+ public static final int WEBVIEW_LOAD_START = 0;
+ public static final int CREATE_CONTEXT_START = 1;
+ public static final int CREATE_CONTEXT_END = 2;
+ public static final int ADD_ASSETS_START = 3;
+ public static final int ADD_ASSETS_END = 4;
+ public static final int GET_CLASS_LOADER_START = 5;
+ public static final int GET_CLASS_LOADER_END = 6;
+ public static final int NATIVE_LOAD_START = 7;
+ public static final int NATIVE_LOAD_END = 8;
+ public static final int PROVIDER_CLASS_FOR_NAME_START = 9;
+ public static final int PROVIDER_CLASS_FOR_NAME_END = 10;
+ private static final int TIMESTAMPS_SIZE = 11;
+
+ // WebView startup timestamps. To access elements use {@link Timestamp}.
+ private static long[] sTimestamps = new long[TIMESTAMPS_SIZE];
+
// Error codes for loadWebViewNativeLibraryFromPackage
public static final int LIBLOAD_SUCCESS = 0;
public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
@@ -230,6 +261,7 @@
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
+ sTimestamps[WEBVIEW_LOAD_START] = System.currentTimeMillis();
final int uid = android.os.Process.myUid();
if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
|| uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
@@ -369,6 +401,7 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
+ sTimestamps[CREATE_CONTEXT_START] = System.currentTimeMillis();
try {
// Construct an app context to load the Java code into the current app.
Context webViewContext = initialApplication.createApplicationContext(
@@ -377,6 +410,7 @@
sPackageInfo = newPackageInfo;
return webViewContext;
} finally {
+ sTimestamps[CREATE_CONTEXT_END] = System.currentTimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (RemoteException | PackageManager.NameNotFoundException e) {
@@ -402,20 +436,26 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {
+ sTimestamps[ADD_ASSETS_START] = System.currentTimeMillis();
for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) {
initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath);
}
+ sTimestamps[ADD_ASSETS_END] = sTimestamps[GET_CLASS_LOADER_START] =
+ System.currentTimeMillis();
ClassLoader clazzLoader = webViewContext.getClassLoader();
-
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
+ sTimestamps[GET_CLASS_LOADER_END] = sTimestamps[NATIVE_LOAD_START] =
+ System.currentTimeMillis();
WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
getWebViewLibrary(sPackageInfo.applicationInfo));
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
-
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
+ sTimestamps[NATIVE_LOAD_END] = sTimestamps[PROVIDER_CLASS_FOR_NAME_START] =
+ System.currentTimeMillis();
try {
return getWebViewProviderClass(clazzLoader);
} finally {
+ sTimestamps[PROVIDER_CLASS_FOR_NAME_END] = System.currentTimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (ClassNotFoundException e) {
@@ -477,4 +517,9 @@
return IWebViewUpdateService.Stub.asInterface(
ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
}
+
+ @NonNull
+ static long[] getTimestamps() {
+ return sTimestamps;
+ }
}
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index ffdb89d..93b2d8a 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -16,6 +16,8 @@
package android.widget;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -25,15 +27,22 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.RemotableViewMethod;
import android.view.View;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import java.time.Clock;
+import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
+import java.util.Formatter;
+import java.util.Locale;
/**
* This widget display an analogic clock with two hands for hours and
@@ -42,25 +51,36 @@
* @attr ref android.R.styleable#AnalogClock_dial
* @attr ref android.R.styleable#AnalogClock_hand_hour
* @attr ref android.R.styleable#AnalogClock_hand_minute
+ * @attr ref android.R.styleable#AnalogClock_hand_second
+ * @attr ref android.R.styleable#AnalogClock_timeZone
* @deprecated This widget is no longer supported.
*/
@RemoteView
@Deprecated
public class AnalogClock extends View {
+ private static final String LOG_TAG = "AnalogClock";
+ /** How often the clock should refresh to make the seconds hand advance at ~15 FPS. */
+ private static final long SECONDS_TICK_FREQUENCY_MS = 1000 / 15;
+
private Clock mClock;
+ @Nullable
+ private ZoneId mTimeZone;
@UnsupportedAppUsage
private Drawable mHourHand;
@UnsupportedAppUsage
private Drawable mMinuteHand;
+ @Nullable
+ private Drawable mSecondHand;
@UnsupportedAppUsage
private Drawable mDial;
private int mDialWidth;
private int mDialHeight;
- private boolean mAttached;
+ private boolean mVisible;
+ private float mSeconds;
private float mMinutes;
private float mHour;
private boolean mChanged;
@@ -101,18 +121,111 @@
mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute);
}
- mClock = Clock.systemDefaultZone();
+ mSecondHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_second);
+
+ mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone));
+ createClock();
mDialWidth = mDial.getIntrinsicWidth();
mDialHeight = mDial.getIntrinsicHeight();
}
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
+ /** Sets the dial of the clock to the specified Icon. */
+ @RemotableViewMethod
+ public void setDial(@NonNull Icon icon) {
+ mDial = icon.loadDrawable(getContext());
+ mDialWidth = mDial.getIntrinsicWidth();
+ mDialHeight = mDial.getIntrinsicHeight();
- if (!mAttached) {
- mAttached = true;
+ mChanged = true;
+ invalidate();
+ }
+
+ /** Sets the hour hand of the clock to the specified Icon. */
+ @RemotableViewMethod
+ public void setHourHand(@NonNull Icon icon) {
+ mHourHand = icon.loadDrawable(getContext());
+
+ mChanged = true;
+ invalidate();
+ }
+
+ /** Sets the minute hand of the clock to the specified Icon. */
+ @RemotableViewMethod
+ public void setMinuteHand(@NonNull Icon icon) {
+ mMinuteHand = icon.loadDrawable(getContext());
+
+ mChanged = true;
+ invalidate();
+ }
+
+ /**
+ * Sets the second hand of the clock to the specified Icon, or hides the second hand if it is
+ * null.
+ */
+ @RemotableViewMethod
+ public void setSecondHand(@Nullable Icon icon) {
+ mSecondHand = icon == null ? null : icon.loadDrawable(getContext());
+ mSecondsTick.run();
+
+ mChanged = true;
+ invalidate();
+ }
+
+ /**
+ * 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,
+ * as set by the user, must be used
+ *
+ * @see java.util.TimeZone
+ * @see java.util.TimeZone#getAvailableIDs()
+ * @see #setTimeZone(String)
+ */
+ @InspectableProperty
+ @Nullable
+ public String getTimeZone() {
+ ZoneId zoneId = mTimeZone;
+ return zoneId == null ? null : zoneId.getId();
+ }
+
+ /**
+ * Sets the specified time zone to use in this clock. When the time zone
+ * is set through this method, system time zone changes (when the user
+ * sets the time zone in settings for instance) will be ignored.
+ *
+ * @param timeZone The desired time zone's ID as specified in {@link java.util.TimeZone}
+ * or null to user the time zone specified by the user
+ * (system time zone)
+ *
+ * @see #getTimeZone()
+ * @see java.util.TimeZone#getAvailableIDs()
+ * @see java.util.TimeZone#getTimeZone(String)
+ *
+ * @attr ref android.R.styleable#AnalogClock_timeZone
+ */
+ @RemotableViewMethod
+ public void setTimeZone(@Nullable String timeZone) {
+ mTimeZone = toZoneId(timeZone);
+
+ createClock();
+ onTimeChanged();
+ }
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+
+ if (isVisible) {
+ onVisible();
+ } else {
+ onInvisible();
+ }
+ }
+
+ private void onVisible() {
+ if (!mVisible) {
+ mVisible = true;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
@@ -128,24 +241,25 @@
// user not the one the context is for.
getContext().registerReceiverAsUser(mIntentReceiver,
android.os.Process.myUserHandle(), filter, null, getHandler());
+
+ mSecondsTick.run();
}
// NOTE: It's safe to do these after registering the receiver since the receiver always runs
// in the main thread, therefore the receiver can't run before this method returns.
- // The time zone may have changed while the receiver wasn't registered, so update the Time
- mClock = Clock.systemDefaultZone();
+ // The time zone may have changed while the receiver wasn't registered, so update the clock.
+ createClock();
// Make sure we update to the current time
onTimeChanged();
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAttached) {
+ private void onInvisible() {
+ if (mVisible) {
getContext().unregisterReceiver(mIntentReceiver);
- mAttached = false;
+ removeCallbacks(mSecondsTick);
+ mVisible = false;
}
}
@@ -237,6 +351,20 @@
minuteHand.draw(canvas);
canvas.restore();
+ final Drawable secondHand = mSecondHand;
+ if (secondHand != null) {
+ canvas.save();
+ canvas.rotate(mSeconds / 60.0f * 360.0f, x, y);
+
+ if (changed) {
+ w = secondHand.getIntrinsicWidth();
+ h = secondHand.getIntrinsicHeight();
+ secondHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
+ }
+ secondHand.draw(canvas);
+ canvas.restore();
+ }
+
if (scaled) {
canvas.restore();
}
@@ -250,6 +378,7 @@
int minute = localDateTime.getMinute();
int second = localDateTime.getSecond();
+ mSeconds = second + localDateTime.getNano() / 1_000_000_000f;
mMinutes = minute + second / 60.0f;
mHour = hour + mMinutes / 60.0f;
mChanged = true;
@@ -261,8 +390,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
- mClock = Clock.system(ZoneId.of(tz));
+ createClock();
}
onTimeChanged();
@@ -271,9 +399,41 @@
}
};
+ private final Runnable mSecondsTick = new Runnable() {
+ @Override
+ public void run() {
+ if (!mVisible || mSecondHand == null) {
+ return;
+ }
+
+ onTimeChanged();
+
+ invalidate();
+
+ postDelayed(this, SECONDS_TICK_FREQUENCY_MS);
+ }
+ };
+
+ private void createClock() {
+ ZoneId zoneId = mTimeZone;
+ if (zoneId == null) {
+ mClock = Clock.systemDefaultZone();
+ } else {
+ mClock = Clock.system(zoneId);
+ }
+ }
+
private void updateContentDescription(long timeMillis) {
final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR;
- String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags);
+ String contentDescription =
+ DateUtils.formatDateRange(
+ mContext,
+ new Formatter(new StringBuilder(50), Locale.getDefault()),
+ timeMillis /* startMillis */,
+ timeMillis /* endMillis */,
+ flags,
+ getTimeZone())
+ .toString();
setContentDescription(contentDescription);
}
@@ -284,4 +444,22 @@
Instant instant = Instant.ofEpochMilli(timeMillis);
return LocalDateTime.ofInstant(instant, zoneId);
}
+
+ /**
+ * Tries to parse a {@link ZoneId} from {@code timeZone}, returning null if it is null or there
+ * is an error parsing.
+ */
+ @Nullable
+ private static ZoneId toZoneId(@Nullable String timeZone) {
+ if (timeZone == null) {
+ return null;
+ }
+
+ try {
+ return ZoneId.of(timeZone);
+ } catch (DateTimeException e) {
+ Log.w(LOG_TAG, "Failed to parse time zone from " + timeZone, e);
+ return null;
+ }
+ }
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 26dd5e3..012352d 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1705,15 +1705,23 @@
}
private void updateFloatingToolbarVisibility(MotionEvent event) {
- if (mTextActionMode != null) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_MOVE:
- hideFloatingToolbar(ActionMode.DEFAULT_HIDE_DURATION);
- break;
- case MotionEvent.ACTION_UP: // fall through
- case MotionEvent.ACTION_CANCEL:
+ if (mTextActionMode == null) {
+ return;
+ }
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_MOVE:
+ hideFloatingToolbar(ActionMode.DEFAULT_HIDE_DURATION);
+ break;
+ case MotionEvent.ACTION_UP: // fall through
+ case MotionEvent.ACTION_CANCEL:
+ final SelectionModifierCursorController selectionController =
+ getSelectionController();
+ final InsertionPointCursorController insertionController = getInsertionController();
+ if ((selectionController != null && selectionController.isCursorBeingModified())
+ || (insertionController != null
+ && insertionController.isCursorBeingModified())) {
showFloatingToolbar();
- }
+ }
}
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 8dafc5d..b47a0ac 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,6 +17,7 @@
package android.widget;
import android.annotation.ColorInt;
+import android.annotation.ColorRes;
import android.annotation.DimenRes;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
@@ -25,6 +26,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
+import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -63,6 +65,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -180,6 +183,8 @@
private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
private static final int SET_INT_TAG_TAG = 22;
private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
+ private static final int RESOURCE_REFLECTION_ACTION_TAG = 24;
+ private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
@@ -980,6 +985,45 @@
return rect;
}
+ private static Class<?> getParameterType(int type) {
+ switch (type) {
+ case BaseReflectionAction.BOOLEAN:
+ return boolean.class;
+ case BaseReflectionAction.BYTE:
+ return byte.class;
+ case BaseReflectionAction.SHORT:
+ return short.class;
+ case BaseReflectionAction.INT:
+ return int.class;
+ case BaseReflectionAction.LONG:
+ return long.class;
+ case BaseReflectionAction.FLOAT:
+ return float.class;
+ case BaseReflectionAction.DOUBLE:
+ return double.class;
+ case BaseReflectionAction.CHAR:
+ return char.class;
+ case BaseReflectionAction.STRING:
+ return String.class;
+ case BaseReflectionAction.CHAR_SEQUENCE:
+ return CharSequence.class;
+ case BaseReflectionAction.URI:
+ return Uri.class;
+ case BaseReflectionAction.BITMAP:
+ return Bitmap.class;
+ case BaseReflectionAction.BUNDLE:
+ return Bundle.class;
+ case BaseReflectionAction.INTENT:
+ return Intent.class;
+ case BaseReflectionAction.COLOR_STATE_LIST:
+ return ColorStateList.class;
+ case BaseReflectionAction.ICON:
+ return Icon.class;
+ default:
+ return null;
+ }
+ }
+
private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
boolean async) {
MethodArgs result;
@@ -1282,7 +1326,8 @@
@Override
public void apply(View root, ViewGroup rootParent,
OnClickHandler handler) throws ActionException {
- ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
+ ReflectionAction ra = new ReflectionAction(viewId, methodName,
+ BaseReflectionAction.BITMAP,
bitmap);
ra.apply(root, rootParent, handler);
}
@@ -1301,7 +1346,7 @@
/**
* Base class for the reflection actions.
*/
- private final class ReflectionAction extends Action {
+ private abstract class BaseReflectionAction extends Action {
static final int BOOLEAN = 1;
static final int BYTE = 2;
static final int SHORT = 3;
@@ -1324,17 +1369,14 @@
@UnsupportedAppUsage
String methodName;
int type;
- @UnsupportedAppUsage
- Object value;
- ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
+ BaseReflectionAction(@IdRes int viewId, String methodName, int type) {
this.viewId = viewId;
this.methodName = methodName;
this.type = type;
- this.value = value;
}
- ReflectionAction(Parcel in) {
+ BaseReflectionAction(Parcel in) {
this.viewId = in.readInt();
this.methodName = in.readString8();
this.type = in.readInt();
@@ -1343,7 +1385,125 @@
Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
+ " methodName=" + this.methodName + " type=" + this.type);
}
+ }
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(this.viewId);
+ out.writeString8(this.methodName);
+ out.writeInt(this.type);
+ }
+
+ /**
+ * Returns the value to use as parameter for the method.
+ *
+ * The view might be passed as {@code null} if the parameter value is requested outside of
+ * inflation. If the parameter cannot be determined at that time, the method should return
+ * {@code null} but not raise any exception.
+ */
+ @Nullable
+ protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
+
+ @Override
+ public final void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ final View view = root.findViewById(viewId);
+ if (view == null) return;
+
+ Class<?> param = getParameterType(this.type);
+ if (param == null) {
+ throw new ActionException("bad type: " + this.type);
+ }
+ Object value = getParameterValue(view);
+ try {
+ getMethod(view, this.methodName, param, false /* async */).invoke(view, value);
+ } catch (Throwable ex) {
+ throw new ActionException(ex);
+ }
+ }
+
+ @Override
+ public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
+ OnClickHandler handler) {
+ final View view = root.findViewById(viewId);
+ if (view == null) return ACTION_NOOP;
+
+ Class<?> param = getParameterType(this.type);
+ if (param == null) {
+ throw new ActionException("bad type: " + this.type);
+ }
+
+ Object value = getParameterValue(view);
+ try {
+ MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
+
+ if (method != null) {
+ Runnable endAction = (Runnable) method.invoke(view, value);
+ if (endAction == null) {
+ return ACTION_NOOP;
+ }
+ // Special case view stub
+ if (endAction instanceof ViewStub.ViewReplaceRunnable) {
+ root.createTree();
+ // Replace child tree
+ root.findViewTreeById(viewId).replaceView(
+ ((ViewStub.ViewReplaceRunnable) endAction).view);
+ }
+ return new RunnableAction(endAction);
+ }
+ } catch (Throwable ex) {
+ throw new ActionException(ex);
+ }
+
+ return this;
+ }
+
+ public final int mergeBehavior() {
+ // smoothScrollBy is cumulative, everything else overwites.
+ if (methodName.equals("smoothScrollBy")) {
+ return MERGE_APPEND;
+ } else {
+ return MERGE_REPLACE;
+ }
+ }
+
+ @Override
+ public final String getUniqueKey() {
+ // Each type of reflection action corresponds to a setter, so each should be seen as
+ // unique from the standpoint of merging.
+ return super.getUniqueKey() + this.methodName + this.type;
+ }
+
+ @Override
+ public final boolean prefersAsyncApply() {
+ return this.type == URI || this.type == ICON;
+ }
+
+ @Override
+ public final void visitUris(@NonNull Consumer<Uri> visitor) {
+ switch (this.type) {
+ case URI:
+ final Uri uri = (Uri) getParameterValue(null);
+ if (uri != null) visitor.accept(uri);
+ break;
+ case ICON:
+ final Icon icon = (Icon) getParameterValue(null);
+ if (icon != null) visitIconUri(icon, visitor);
+ break;
+ }
+ }
+ }
+
+ /** Class for the reflection actions. */
+ private final class ReflectionAction extends BaseReflectionAction {
+ @UnsupportedAppUsage
+ Object value;
+
+ ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
+ super(viewId, methodName, type);
+ this.value = value;
+ }
+
+ ReflectionAction(Parcel in) {
+ super(in);
// For some values that may have been null, we first check a flag to see if they were
// written to the parcel.
switch (this.type) {
@@ -1354,7 +1514,7 @@
this.value = in.readByte();
break;
case SHORT:
- this.value = (short)in.readInt();
+ this.value = (short) in.readInt();
break;
case INT:
this.value = in.readInt();
@@ -1369,7 +1529,7 @@
this.value = in.readDouble();
break;
case CHAR:
- this.value = (char)in.readInt();
+ this.value = (char) in.readInt();
break;
case STRING:
this.value = in.readString8();
@@ -1400,15 +1560,7 @@
}
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(this.viewId);
- out.writeString8(this.methodName);
- out.writeInt(this.type);
- //noinspection ConstantIfStatement
- if (false) {
- Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
- + " methodName=" + this.methodName + " type=" + this.type);
- }
-
+ super.writeToParcel(out, flags);
// For some values which are null, we record an integer flag to indicate whether
// we have written a valid value to the parcel.
switch (this.type) {
@@ -1434,13 +1586,13 @@
out.writeDouble((Double) this.value);
break;
case CHAR:
- out.writeInt((int)((Character)this.value).charValue());
+ out.writeInt((int) ((Character) this.value).charValue());
break;
case STRING:
- out.writeString8((String)this.value);
+ out.writeString8((String) this.value);
break;
case CHAR_SEQUENCE:
- TextUtils.writeToParcel((CharSequence)this.value, out, flags);
+ TextUtils.writeToParcel((CharSequence) this.value, out, flags);
break;
case BUNDLE:
out.writeBundle((Bundle) this.value);
@@ -1457,135 +1609,134 @@
}
}
- private Class<?> getParameterType() {
- switch (this.type) {
- case BOOLEAN:
- return boolean.class;
- case BYTE:
- return byte.class;
- case SHORT:
- return short.class;
- case INT:
- return int.class;
- case LONG:
- return long.class;
- case FLOAT:
- return float.class;
- case DOUBLE:
- return double.class;
- case CHAR:
- return char.class;
- case STRING:
- return String.class;
- case CHAR_SEQUENCE:
- return CharSequence.class;
- case URI:
- return Uri.class;
- case BITMAP:
- return Bitmap.class;
- case BUNDLE:
- return Bundle.class;
- case INTENT:
- return Intent.class;
- case COLOR_STATE_LIST:
- return ColorStateList.class;
- case ICON:
- return Icon.class;
- default:
- return null;
- }
- }
-
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final View view = root.findViewById(viewId);
- if (view == null) return;
-
- Class<?> param = getParameterType();
- if (param == null) {
- throw new ActionException("bad type: " + this.type);
- }
- try {
- getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
- } catch (Throwable ex) {
- throw new ActionException(ex);
- }
- }
-
- @Override
- public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
- final View view = root.findViewById(viewId);
- if (view == null) return ACTION_NOOP;
-
- Class<?> param = getParameterType();
- if (param == null) {
- throw new ActionException("bad type: " + this.type);
- }
-
- try {
- MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
-
- if (method != null) {
- Runnable endAction = (Runnable) method.invoke(view, this.value);
- if (endAction == null) {
- return ACTION_NOOP;
- } else {
- // Special case view stub
- if (endAction instanceof ViewStub.ViewReplaceRunnable) {
- root.createTree();
- // Replace child tree
- root.findViewTreeById(viewId).replaceView(
- ((ViewStub.ViewReplaceRunnable) endAction).view);
- }
- return new RunnableAction(endAction);
- }
- }
- } catch (Throwable ex) {
- throw new ActionException(ex);
- }
-
- return this;
- }
-
- public int mergeBehavior() {
- // smoothScrollBy is cumulative, everything else overwites.
- if (methodName.equals("smoothScrollBy")) {
- return MERGE_APPEND;
- } else {
- return MERGE_REPLACE;
- }
+ protected Object getParameterValue(View view) throws ActionException {
+ return this.value;
}
@Override
public int getActionTag() {
return REFLECTION_ACTION_TAG;
}
+ }
- @Override
- public String getUniqueKey() {
- // Each type of reflection action corresponds to a setter, so each should be seen as
- // unique from the standpoint of merging.
- return super.getUniqueKey() + this.methodName + this.type;
+ private final class ResourceReflectionAction extends BaseReflectionAction {
+
+ static final int DIMEN_RESOURCE = 1;
+ static final int COLOR_RESOURCE = 2;
+ static final int STRING_RESOURCE = 3;
+
+ private final int mResourceType;
+ private final int mResId;
+
+ ResourceReflectionAction(@IdRes int viewId, String methodName, int parameterType,
+ int resourceType, int resId) {
+ super(viewId, methodName, parameterType);
+ this.mResourceType = resourceType;
+ this.mResId = resId;
+ }
+
+ ResourceReflectionAction(Parcel in) {
+ super(in);
+ this.mResourceType = in.readInt();
+ this.mResId = in.readInt();
}
@Override
- public boolean prefersAsyncApply() {
- return this.type == URI || this.type == ICON;
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(this.mResourceType);
+ dest.writeInt(this.mResId);
}
@Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- switch (this.type) {
- case URI:
- final Uri uri = (Uri) this.value;
- visitor.accept(uri);
- break;
- case ICON:
- final Icon icon = (Icon) this.value;
- visitIconUri(icon, visitor);
- break;
+ protected @NonNull Object getParameterValue(View view) throws ActionException {
+ Resources resources = view.getContext().getResources();
+ try {
+ switch (this.mResourceType) {
+ case DIMEN_RESOURCE:
+ if (this.type == BaseReflectionAction.INT) {
+ return resources.getDimensionPixelSize(this.mResId);
+ }
+ return resources.getDimension(this.mResId);
+ case COLOR_RESOURCE:
+ switch(this.type) {
+ case BaseReflectionAction.INT:
+ return view.getContext().getColor(this.mResId);
+ case BaseReflectionAction.COLOR_STATE_LIST:
+ return view.getContext().getColorStateList(this.mResId);
+ default:
+ throw new ActionException(
+ "color resources must be used as int or ColorStateList, "
+ + "not " + this.type);
+ }
+ case STRING_RESOURCE:
+ return resources.getText(this.mResId);
+ default:
+ throw new ActionException("unknown resource type: " + this.mResourceType);
+ }
+ } catch (Throwable t) {
+ throw new ActionException(t);
}
}
+
+ @Override
+ public int getActionTag() {
+ return RESOURCE_REFLECTION_ACTION_TAG;
+ }
+ }
+
+ private final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction {
+
+ private final float mValue;
+ @ComplexDimensionUnit
+ private final int mUnit;
+
+ ComplexUnitDimensionReflectionAction(int viewId, String methodName, int parameterType,
+ float value, @ComplexDimensionUnit int unit) {
+ super(viewId, methodName, parameterType);
+ this.mValue = value;
+ this.mUnit = unit;
+ }
+
+ ComplexUnitDimensionReflectionAction(Parcel in) {
+ super(in);
+ this.mValue = in.readFloat();
+ this.mUnit = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeFloat(this.mValue);
+ dest.writeInt(this.mUnit);
+ }
+
+ @Override
+ protected Object getParameterValue(View view) throws ActionException {
+ DisplayMetrics dm = view.getContext().getResources().getDisplayMetrics();
+ try {
+ int data = TypedValue.createComplexDimension(this.mValue, this.mUnit);
+ switch (this.type) {
+ case ReflectionAction.INT:
+ return TypedValue.complexToDimensionPixelSize(data, dm);
+ case ReflectionAction.FLOAT:
+ return TypedValue.complexToDimension(data, dm);
+ default:
+ throw new ActionException(
+ "parameter type must be INT or FLOAT, not " + this.type);
+ }
+ } catch (ActionException ex) {
+ throw ex;
+ } catch (Throwable t) {
+ throw new ActionException(t);
+ }
+ }
+
+ @Override
+ public int getActionTag() {
+ return COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG;
+ }
}
/**
@@ -2611,6 +2762,10 @@
return new SetIntTagAction(parcel);
case REMOVE_FROM_PARENT_ACTION_TAG:
return new RemoveFromParentAction(parcel);
+ case RESOURCE_REFLECTION_ACTION_TAG:
+ return new ResourceReflectionAction(parcel);
+ case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG:
+ return new ComplexUnitDimensionReflectionAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -3113,7 +3268,7 @@
*/
public void setProgressTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setProgressTintList",
- ReflectionAction.COLOR_STATE_LIST, tint));
+ BaseReflectionAction.COLOR_STATE_LIST, tint));
}
/**
@@ -3125,7 +3280,7 @@
*/
public void setProgressBackgroundTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
- ReflectionAction.COLOR_STATE_LIST, tint));
+ BaseReflectionAction.COLOR_STATE_LIST, tint));
}
/**
@@ -3137,7 +3292,7 @@
*/
public void setProgressIndeterminateTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
- ReflectionAction.COLOR_STATE_LIST, tint));
+ BaseReflectionAction.COLOR_STATE_LIST, tint));
}
/**
@@ -3159,8 +3314,8 @@
* @param colors the text colors to set
*/
public void setTextColor(@IdRes int viewId, ColorStateList colors) {
- addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
- colors));
+ addAction(new ReflectionAction(viewId, "setTextColor",
+ BaseReflectionAction.COLOR_STATE_LIST, colors));
}
/**
@@ -3353,7 +3508,7 @@
* @param value The value to pass to the method.
*/
public void setBoolean(@IdRes int viewId, String methodName, boolean value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BOOLEAN, value));
}
/**
@@ -3364,7 +3519,7 @@
* @param value The value to pass to the method.
*/
public void setByte(@IdRes int viewId, String methodName, byte value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BYTE, value));
}
/**
@@ -3375,7 +3530,7 @@
* @param value The value to pass to the method.
*/
public void setShort(@IdRes int viewId, String methodName, short value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.SHORT, value));
}
/**
@@ -3386,10 +3541,59 @@
* @param value The value to pass to the method.
*/
public void setInt(@IdRes int viewId, String methodName, int value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INT, value));
}
/**
+ * Call a method taking one int, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param dimenResource The resource to resolve and pass as argument to the method.
+ */
+ public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
+ @DimenRes int dimenResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
+ ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
+ }
+
+ /**
+ * Call a method taking one int, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the specified dimension at the time of inflation.
+ *
+ * @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 of the dimension.
+ * @param unit The unit in which the value is specified.
+ */
+ public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
+ float value, @ComplexDimensionUnit int unit) {
+ addAction(new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.INT,
+ value, unit));
+ }
+
+ /**
+ * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
+ *
+ * The ColorStateList will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param colorResource The resource to resolve and pass as argument to the method.
+ */
+ public void setColor(@IdRes int viewId, @NonNull String methodName,
+ @ColorRes int colorResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
+ ResourceReflectionAction.COLOR_RESOURCE, colorResource));
+ }
+
+
+ /**
* 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.
@@ -3399,10 +3603,25 @@
* @hide
*/
public void setColorStateList(@IdRes int viewId, String methodName, ColorStateList value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.COLOR_STATE_LIST,
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.COLOR_STATE_LIST,
value));
}
+ /**
+ * 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.
+ * @param methodName The name of the method to call.
+ * @param colorResource The resource to resolve and pass as argument to the method.
+ */
+ public void setColorStateList(@IdRes int viewId, @NonNull String methodName,
+ @ColorRes int colorResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName,
+ BaseReflectionAction.COLOR_STATE_LIST, ResourceReflectionAction.COLOR_RESOURCE,
+ colorResource));
+ }
/**
* Call a method taking one long on a view in the layout for this RemoteViews.
@@ -3412,7 +3631,7 @@
* @param value The value to pass to the method.
*/
public void setLong(@IdRes int viewId, String methodName, long value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.LONG, value));
}
/**
@@ -3423,7 +3642,41 @@
* @param value The value to pass to the method.
*/
public void setFloat(@IdRes int viewId, String methodName, float value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT, value));
+ }
+
+ /**
+ * Call a method taking one float, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param dimenResource The resource to resolve and pass as argument to the method.
+ */
+ public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
+ @DimenRes int dimenResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT,
+ ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
+ }
+
+ /**
+ * Call a method taking one float, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the specified dimension at the time of inflation.
+ *
+ * @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 of the dimension.
+ * @param unit The unit in which the value is specified.
+ */
+ public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
+ float value, @ComplexDimensionUnit int unit) {
+ addAction(
+ new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.FLOAT,
+ value, unit));
}
/**
@@ -3434,7 +3687,7 @@
* @param value The value to pass to the method.
*/
public void setDouble(@IdRes int viewId, String methodName, double value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.DOUBLE, value));
}
/**
@@ -3445,7 +3698,7 @@
* @param value The value to pass to the method.
*/
public void setChar(@IdRes int viewId, String methodName, char value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR, value));
}
/**
@@ -3456,7 +3709,7 @@
* @param value The value to pass to the method.
*/
public void setString(@IdRes int viewId, String methodName, String value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.STRING, value));
}
/**
@@ -3467,7 +3720,24 @@
* @param value The value to pass to the method.
*/
public void setCharSequence(@IdRes int viewId, String methodName, CharSequence value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
+ value));
+ }
+
+ /**
+ * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
+ *
+ * The CharSequence will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param stringResource The resource to resolve and pass as argument to the method.
+ */
+ public void setCharSequence(@IdRes int viewId, @NonNull String methodName,
+ @StringRes int stringResource) {
+ addAction(
+ new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
+ ResourceReflectionAction.STRING_RESOURCE, stringResource));
}
/**
@@ -3485,7 +3755,7 @@
value.checkFileUriExposed("RemoteViews.setUri()");
}
}
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.URI, value));
}
/**
@@ -3510,7 +3780,7 @@
* @param value The value to pass to the method.
*/
public void setBundle(@IdRes int viewId, String methodName, Bundle value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BUNDLE, value));
}
/**
@@ -3521,7 +3791,7 @@
* @param value The {@link android.content.Intent} to pass the method.
*/
public void setIntent(@IdRes int viewId, String methodName, Intent value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INTENT, value));
}
/**
@@ -3532,7 +3802,7 @@
* @param value The {@link android.graphics.drawable.Icon} to pass the method.
*/
public void setIcon(@IdRes int viewId, String methodName, Icon value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.ICON, value));
}
/**
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 97d98fd..794b642 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -220,29 +220,26 @@
}
void onPerformSpellCheck() {
- final int selectionStart = mTextView.getSelectionStart();
- final int selectionEnd = mTextView.getSelectionEnd();
- final int selectionRangeStart;
- final int selectionRangeEnd;
- if (selectionStart < selectionEnd) {
- selectionRangeStart = selectionStart;
- selectionRangeEnd = selectionEnd;
- } else {
- selectionRangeStart = selectionEnd;
- selectionRangeEnd = selectionStart;
- }
- // Expand the range so that it (hopefully) includes the current sentence.
- final int start = Math.max(0, selectionRangeStart - MIN_SENTENCE_LENGTH);
- final int end = Math.min(mTextView.length(), selectionRangeEnd + MIN_SENTENCE_LENGTH);
+ // Triggers full content spell check.
+ final int start = 0;
+ final int end = mTextView.length();
if (DBG) {
Log.d(TAG, "performSpellCheckAroundSelection: " + start + ", " + end);
}
- spellCheck(start, end);
+ spellCheck(start, end, /* forceCheckWhenEditingWord= */ true);
}
public void spellCheck(int start, int end) {
+ spellCheck(start, end, /* forceCheckWhenEditingWord= */ false);
+ }
+
+ /**
+ * Requests to do spell check for text in the range (start, end).
+ */
+ public void spellCheck(int start, int end, boolean forceCheckWhenEditingWord) {
if (DBG) {
- Log.d(TAG, "Start spell-checking: " + start + ", " + end);
+ Log.d(TAG, "Start spell-checking: " + start + ", " + end + ", "
+ + forceCheckWhenEditingWord);
}
final Locale locale = mTextView.getSpellCheckerLocale();
final boolean isSessionActive = isSessionActive();
@@ -267,7 +264,7 @@
for (int i = 0; i < length; i++) {
final SpellParser spellParser = mSpellParsers[i];
if (spellParser.isFinished()) {
- spellParser.parse(start, end);
+ spellParser.parse(start, end, forceCheckWhenEditingWord);
return;
}
}
@@ -282,10 +279,14 @@
SpellParser spellParser = new SpellParser();
mSpellParsers[length] = spellParser;
- spellParser.parse(start, end);
+ spellParser.parse(start, end, forceCheckWhenEditingWord);
}
private void spellCheck() {
+ spellCheck(/* forceCheckWhenEditingWord= */ false);
+ }
+
+ private void spellCheck(boolean forceCheckWhenEditingWord) {
if (mSpellCheckerSession == null) return;
Editable editable = (Editable) mTextView.getText();
@@ -295,6 +296,12 @@
TextInfo[] textInfos = new TextInfo[mLength];
int textInfosCount = 0;
+ if (DBG) {
+ Log.d(TAG, "forceCheckWhenEditingWord=" + forceCheckWhenEditingWord
+ + ", mLength=" + mLength + ", cookie = " + mCookie
+ + ", sel start = " + selectionStart + ", sel end = " + selectionEnd);
+ }
+
for (int i = 0; i < mLength; i++) {
final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
if (mIds[i] < 0 || spellCheckSpan.isSpellCheckInProgress()) continue;
@@ -319,7 +326,7 @@
} else {
isEditing = selectionEnd < start || selectionStart > end;
}
- if (start >= 0 && end > start && isEditing) {
+ if (start >= 0 && end > start && (forceCheckWhenEditingWord || isEditing)) {
spellCheckSpan.setSpellCheckInProgress(true);
final TextInfo textInfo = new TextInfo(editable, start, end, mCookie, mIds[i]);
textInfos[textInfosCount++] = textInfo;
@@ -546,7 +553,11 @@
private class SpellParser {
private Object mRange = new Object();
- public void parse(int start, int end) {
+ // Forces to do spell checker even user is editing the word.
+ private boolean mForceCheckWhenEditingWord;
+
+ public void parse(int start, int end, boolean forceCheckWhenEditingWord) {
+ mForceCheckWhenEditingWord = forceCheckWhenEditingWord;
final int max = mTextView.length();
final int parseEnd;
if (end > max) {
@@ -567,6 +578,7 @@
public void stop() {
removeRangeSpan((Editable) mTextView.getText());
+ mForceCheckWhenEditingWord = false;
}
private void setRangeSpan(Editable editable, int start, int end) {
@@ -617,7 +629,7 @@
if (DBG) {
Log.i(TAG, "No more spell check.");
}
- removeRangeSpan(editable);
+ stop();
return;
}
@@ -649,7 +661,7 @@
if (DBG) {
Log.i(TAG, "Incorrect range span.");
}
- removeRangeSpan(editable);
+ stop();
return;
}
do {
@@ -778,7 +790,7 @@
removeRangeSpan(editable);
}
- spellCheck();
+ spellCheck(mForceCheckWhenEditingWord);
}
private <T> void removeSpansAt(Editable editable, int offset, T[] spans) {
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index f29eb39..cdb4762 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.os.IBinder;
@@ -151,6 +152,7 @@
/** Gets direct child tasks (ordered from top-to-bottom) */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
@Nullable
+ @SuppressLint("NullableCollection")
public List<ActivityManager.RunningTaskInfo> getChildTasks(
@NonNull WindowContainerToken parent, @NonNull int[] activityTypes) {
try {
@@ -163,6 +165,7 @@
/** Gets all root tasks on a display (ordered from top-to-bottom) */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
@Nullable
+ @SuppressLint("NullableCollection")
public List<ActivityManager.RunningTaskInfo> getRootTasks(
int displayId, @NonNull int[] activityTypes) {
try {
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 99692d0..7ade05c 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -5,3 +5,4 @@
per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
per-file IVoice* = file:/core/java/android/service/voice/OWNERS
per-file *Hotword* = file:/core/java/android/service/voice/OWNERS
+per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 13358daf..ee98878 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -62,6 +62,11 @@
*/
public static final String ENABLE_NAS_FEEDBACK = "enable_nas_feedback";
+ /**
+ * Whether the Notification Assistant can label a notification not a conversation
+ */
+ public static final String ENABLE_NAS_NOT_CONVERSATION = "enable_nas_not_conversation";
+
// Flags related to screenshot intelligence
/**
@@ -420,6 +425,12 @@
public static final String PIP_STASHING = "pip_stashing";
/**
+ * (float) The threshold velocity to cause PiP to be stashed when flinging from one edge to the
+ * other.
+ */
+ public static final String PIP_STASH_MINIMUM_VELOCITY_THRESHOLD = "pip_velocity_threshold";
+
+ /**
* (float) Bottom height in DP for Back Gesture.
*/
public static final String BACK_GESTURE_BOTTOM_HEIGHT = "back_gesture_bottom_height";
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index af666d8..9a44c05 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -16,6 +16,7 @@
package com.android.internal.content;
+import android.annotation.NonNull;
import android.app.Activity;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -234,7 +235,7 @@
/**
* Called when an existing package is updated or its disabled state changes.
*/
- public void onPackageModified(String packageName) {
+ public void onPackageModified(@NonNull String packageName) {
}
public boolean didSomePackagesChange() {
diff --git a/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
similarity index 78%
rename from graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java
rename to core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
index 7e75c5b..96dac56 100644
--- a/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java
+++ b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package android.graphics.drawable;
+package com.android.internal.graphics.drawable;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
+import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -31,71 +30,59 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RenderNode;
+import android.graphics.drawable.Drawable;
import android.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControl;
-import android.view.View;
import android.view.ViewRootImpl;
+import com.android.internal.R;
+
/**
* A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state
* to SurfaceFlinger.
- *
- * @hide
*/
-@SystemApi
public final class BackgroundBlurDrawable extends Drawable {
+
private static final String TAG = BackgroundBlurDrawable.class.getSimpleName();
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final Aggregator mAggregator;
private final RenderNode mRenderNode;
private final Paint mPaint = new Paint();
private final Path mRectPath = new Path();
private final float[] mTmpRadii = new float[8];
private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion();
- private Aggregator mAggregator;
-
// This will be called from a thread pool.
private final RenderNode.PositionUpdateListener mPositionUpdateListener =
new RenderNode.PositionUpdateListener() {
@Override
public void positionChanged(long frameNumber, int left, int top, int right,
int bottom) {
- if (mAggregator == null) {
+ synchronized (mAggregator) {
mBlurRegion.rect.set(left, top, right, bottom);
- } else {
- synchronized (mAggregator) {
- mBlurRegion.rect.set(left, top, right, bottom);
- mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
- }
+ mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
}
}
@Override
public void positionLost(long frameNumber) {
- if (mAggregator == null) {
+ synchronized (mAggregator) {
mBlurRegion.rect.setEmpty();
- } else {
- synchronized (mAggregator) {
- mBlurRegion.rect.setEmpty();
- mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
- }
+ mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
}
}
};
- @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR)
- public BackgroundBlurDrawable() {
+ private BackgroundBlurDrawable(Aggregator aggregator) {
+ mAggregator = aggregator;
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
mPaint.setColor(Color.TRANSPARENT);
mRenderNode = new RenderNode("BackgroundBlurDrawable");
mRenderNode.addPositionUpdateListener(mPositionUpdateListener);
}
- /**
- * @hide
- */
@Override
public void draw(@NonNull Canvas canvas) {
if (mRectPath.isEmpty() || !isVisible() || getAlpha() == 0) {
@@ -113,9 +100,6 @@
mPaint.setColor(color);
}
- /**
- * @hide
- */
@Override
public boolean setVisible(boolean visible, boolean restart) {
boolean changed = super.setVisible(visible, restart);
@@ -125,9 +109,6 @@
return changed;
}
- /**
- * @hide
- */
@Override
public void setAlpha(int alpha) {
mBlurRegion.alpha = alpha / 255f;
@@ -158,12 +139,12 @@
*/
public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL,
float cornerRadiusBR) {
- maybeRunSynchronized(() -> {
+ synchronized (mAggregator) {
mBlurRegion.cornerRadiusTL = cornerRadiusTL;
mBlurRegion.cornerRadiusTR = cornerRadiusTR;
mBlurRegion.cornerRadiusBL = cornerRadiusBL;
mBlurRegion.cornerRadiusBR = cornerRadiusBR;
- });
+ }
updatePath();
invalidateSelf();
}
@@ -176,13 +157,12 @@
}
private void updatePath() {
- maybeRunSynchronized(() -> {
+ synchronized (mAggregator) {
mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL;
mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR;
mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL;
mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR;
- });
-
+ }
mRectPath.reset();
if (getAlpha() == 0 || !isVisible()) {
return;
@@ -192,62 +172,19 @@
Path.Direction.CW);
}
- /**
- * @hide
- */
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
throw new IllegalArgumentException("not implemented");
}
- /**
- * @hide
- */
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
/**
- * @hide
- */
- @Override
- public void onAttached(@NonNull View v) {
- super.onAttached(v);
- mAggregator = v.getViewRootImpl().getBlurRegionAggregator();
- }
-
- /**
- * @hide
- */
- @Override
- public void onDetached(@NonNull View v) {
- super.onDetached(v);
- mAggregator = null;
- }
-
- /**
- * The Aggregator is called from the RenderThread to aggregate all blur regions and send them
- * to SurfaceFlinger. Since the BackgroundBlurDrawable could be updated at any time from the
- * main thread, we need to synchronize the two threads. The BackgroundBlurDrawable may be
- * instantiated before the ViewRootImpl is created, i.e. before the Aggregator is created.
- * In that case, updates are not synchronized.
- */
- private void maybeRunSynchronized(Runnable r) {
- if (mAggregator == null) {
- r.run();
- } else {
- synchronized (mAggregator) {
- r.run();
- }
- }
- }
-
- /**
* Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a
* message when it's time to propagate them.
- *
- * @hide
*/
public static final class Aggregator {
@@ -262,6 +199,16 @@
}
/**
+ * Creates a blur region with default radius.
+ */
+ public BackgroundBlurDrawable createBackgroundBlurDrawable(Context context) {
+ BackgroundBlurDrawable drawable = new BackgroundBlurDrawable(this);
+ drawable.setBlurRadius(context.getResources().getDimensionPixelSize(
+ R.dimen.default_background_blur_radius));
+ return drawable;
+ }
+
+ /**
* Called from RenderThread only, already locked.
* @param drawable
* @param blurRegion
diff --git a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
index a11c7ef..cafe0de 100644
--- a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
+++ b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
@@ -16,6 +16,7 @@
package com.android.internal.graphics.fonts;
+import android.os.ParcelFileDescriptor;
import android.text.FontConfig;
import android.graphics.fonts.SystemFontState;
@@ -26,4 +27,6 @@
*/
interface IFontManager {
FontConfig getFontConfig();
+
+ int updateFont(in ParcelFileDescriptor fd, in byte[] signature, int baseVersion);
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index e0f9554..3d896c8 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -59,6 +59,7 @@
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
+import com.android.internal.util.PerfettoTrigger;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/com/android/internal/jank/PerfettoTrigger.java b/core/java/com/android/internal/jank/PerfettoTrigger.java
deleted file mode 100644
index 643d24a..0000000
--- a/core/java/com/android/internal/jank/PerfettoTrigger.java
+++ /dev/null
@@ -1,73 +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.
- */
-
-//TODO (165884885): Make PerfettoTrigger more generic and move it to another package.
-package com.android.internal.jank;
-
-import android.annotation.NonNull;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-/**
- * A trigger implementation with perfetto backend.
- * @hide
- */
-public class PerfettoTrigger {
- private static final String TAG = PerfettoTrigger.class.getSimpleName();
- private static final boolean DEBUG = false;
- private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
-
- /**
- * @param triggerName The name of the trigger. Must match the value defined in the AOT
- * Perfetto config.
- */
- public static void trigger(String triggerName) {
- try {
- ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
- if (DEBUG) {
- StringBuilder sb = new StringBuilder();
- for (String arg : pb.command()) {
- sb.append(arg).append(" ");
- }
- Log.d(TAG, "Triggering " + sb.toString());
- }
- Process process = pb.start();
- if (DEBUG) {
- readConsoleOutput(process);
- }
- } catch (IOException | InterruptedException e) {
- Log.w(TAG, "Failed to trigger " + triggerName, e);
- }
- }
-
- private static void readConsoleOutput(@NonNull Process process)
- throws IOException, InterruptedException {
- process.waitFor();
- try (BufferedReader errReader =
- new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
- StringBuilder errLine = new StringBuilder();
- String line;
- while ((line = errReader.readLine()) != null) {
- errLine.append(line).append("\n");
- }
- errLine.append(", code=").append(process.exitValue());
- Log.d(TAG, "err message=" + errLine.toString());
- }
- }
-}
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 6609ebe..8fe17fb 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -54,6 +54,7 @@
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah)
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs);
}
+ // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats.
}
/**
@@ -66,7 +67,8 @@
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType);
- final double powerMah = mPowerEstimator.calculatePower(durationMs);
+ final double powerMah = getMeasuredOrEstimatedPower(
+ batteryStats.getScreenDozeEnergy(), durationMs);
if (powerMah > 0) {
BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0);
bs.usagePowerMah = powerMah;
@@ -79,4 +81,12 @@
private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
return batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000;
}
+
+ private double getMeasuredOrEstimatedPower(long measuredEnergyUJ, long durationMs) {
+ if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
+ return mAhToUJ(measuredEnergyUJ);
+ } else {
+ return mPowerEstimator.calculatePower(durationMs);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index c8805dd..af61f91 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -35,6 +35,8 @@
/**
* Smeared power from screen usage.
* We split the screen usage power and smear them among apps, based on activity time.
+ * The actual screen usage power may be measured or estimated, affecting the granularity and
+ * accuracy of the smearing, but the smearing algorithm is essentially the same.
*/
public double screenPowerMah;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index fcf8bb4..aa5015a 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -37,7 +37,6 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2b034b0..1f7a7aa 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -21,8 +21,6 @@
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
-import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -107,7 +105,7 @@
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.power.MeasuredEnergyStats;
-import com.android.internal.power.MeasuredEnergyStats.EnergyBucket;
+import com.android.internal.power.MeasuredEnergyStats.StandardEnergyBucket;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
@@ -173,7 +171,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 191 + (USE_OLD_HISTORY ? 1000 : 0);
+ static final int VERSION = 193 + (USE_OLD_HISTORY ? 1000 : 0);
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -362,7 +360,6 @@
public interface PlatformIdleStateCallback {
public void fillLowPowerStats(RpmStats rpmStats);
- public String getPlatformLowPowerStats();
public String getSubsystemLowPowerStats();
}
@@ -3482,11 +3479,6 @@
}
if (computeStepDetails) {
if (mPlatformIdleStateCallback != null) {
- mCurHistoryStepDetails.statPlatformIdleState =
- mPlatformIdleStateCallback.getPlatformLowPowerStats();
- if (DEBUG) Slog.i(TAG, "WRITE PlatformIdleState:" +
- mCurHistoryStepDetails.statPlatformIdleState);
-
mCurHistoryStepDetails.statSubsystemPowerState =
mPlatformIdleStateCallback.getSubsystemLowPowerStats();
if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" +
@@ -7167,8 +7159,8 @@
if (mGlobalMeasuredEnergyStats == null) {
return ENERGY_DATA_UNAVAILABLE;
}
- return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy(
- MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+ return mGlobalMeasuredEnergyStats
+ .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
}
@Override
@@ -7176,8 +7168,8 @@
if (mGlobalMeasuredEnergyStats == null) {
return ENERGY_DATA_UNAVAILABLE;
}
- return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy(
- MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
+ return mGlobalMeasuredEnergyStats
+ .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
}
@Override public long getStartClockTime() {
@@ -7941,27 +7933,27 @@
return mUidMeasuredEnergyStats;
}
- /** Adds the given energy to the given energy bucket for this uid. */
- private void addEnergyToEnergyBucketLocked(long energyDeltaUJ,
- @MeasuredEnergyStats.EnergyBucket int energyBucket, boolean accumulate) {
+ /** Adds the given energy to the given standard energy bucket for this uid. */
+ private void addEnergyToStandardBucketLocked(long energyDeltaUJ,
+ @StandardEnergyBucket int energyBucket, boolean accumulate) {
getOrCreateMeasuredEnergyStatsLocked()
- .updateBucket(energyBucket, energyDeltaUJ, accumulate);
+ .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate);
}
/**
- * Returns the energy used by this uid for an energy bucket of interest.
- * @param bucket energy bucket of interest
+ * Returns the energy used by this uid for a standard energy bucket of interest.
+ * @param bucket standard energy bucket of interest
* @return energy (in microjoules) used by this uid for this energy bucket
*/
- public long getMeasuredEnergyMicroJoules(@MeasuredEnergyStats.EnergyBucket int bucket) {
+ public long getMeasuredEnergyMicroJoules(@StandardEnergyBucket int bucket) {
if (mBsi.mGlobalMeasuredEnergyStats == null
- || !mBsi.mGlobalMeasuredEnergyStats.isEnergyBucketSupported(bucket)) {
+ || !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
return ENERGY_DATA_UNAVAILABLE;
}
if (mUidMeasuredEnergyStats == null) {
return 0L; // It is supported, but was never filled, so it must be 0
}
- return mUidMeasuredEnergyStats.getAccumulatedBucketEnergy(bucket);
+ return mUidMeasuredEnergyStats.getAccumulatedStandardBucketEnergy(bucket);
}
/**
@@ -8633,11 +8625,7 @@
@Override
public long getScreenOnEnergy() {
- if (mUidMeasuredEnergyStats == null) {
- return ENERGY_DATA_UNAVAILABLE;
- }
- return mUidMeasuredEnergyStats.getAccumulatedBucketEnergy(
- MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+ return getMeasuredEnergyMicroJoules(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
}
void initNetworkActivityLocked() {
@@ -12406,7 +12394,7 @@
return;
}
- final @EnergyBucket int energyBucket =
+ final @StandardEnergyBucket int energyBucket =
MeasuredEnergyStats.getDisplayEnergyBucket(mScreenStateAtLastEnergyMeasurement);
mScreenStateAtLastEnergyMeasurement = screenState;
@@ -12425,7 +12413,7 @@
return;
}
- mGlobalMeasuredEnergyStats.updateBucket(energyBucket, energyUJ, true);
+ mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ, true);
// Now we blame individual apps, but only if the display was ON.
if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) {
@@ -12463,7 +12451,7 @@
final long appDisplayEnergyMJ =
(totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2))
/ totalFgTimeMs;
- uid.addEnergyToEnergyBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true);
+ uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true);
// To mitigate round-off errors, remove this app from numerator & denominator totals
totalDisplayEnergyMJ -= appDisplayEnergyMJ;
@@ -14148,33 +14136,34 @@
/**
* Initialize the measured energy stats data structures.
*
- * @param supportedEnergyBuckets boolean array indicating which buckets are currently supported
+ * @param supportedStandardBuckets boolean array indicating which {@link StandardEnergyBucket}s
+ * are currently supported.
+ * If null, none are supported (regardless of numCustomBuckets).
+ * @param numCustomBuckets number of custom (OTHER) EnergyConsumers on this device
*/
@GuardedBy("this")
- public void initMeasuredEnergyStatsLocked(boolean[] supportedEnergyBuckets) {
+ public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets,
+ int numCustomBuckets) {
boolean supportedBucketMismatch = false;
mScreenStateAtLastEnergyMeasurement = mScreenState;
- if (supportedEnergyBuckets == null) {
+ if (supportedStandardBuckets == null) {
if (mGlobalMeasuredEnergyStats != null) {
// Measured energy buckets no longer supported, wipe out the existing data.
supportedBucketMismatch = true;
}
} else if (mGlobalMeasuredEnergyStats == null) {
- mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets);
+ mGlobalMeasuredEnergyStats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
return;
} else {
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (mGlobalMeasuredEnergyStats.isEnergyBucketSupported(i)
- != supportedEnergyBuckets[i]) {
- mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets);
- supportedBucketMismatch = true;
- break;
- }
- }
+ supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo(
+ supportedStandardBuckets, numCustomBuckets);
}
if (supportedBucketMismatch) {
+ mGlobalMeasuredEnergyStats = supportedStandardBuckets == null ?
+ null : new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
// Supported energy buckets changed since last boot.
// Existing data is no longer reliable.
resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime());
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index b8c066d..964568c 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.hardware.SensorManager;
-import android.net.ConnectivityManager;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
@@ -57,7 +56,7 @@
mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
- if (!isWifiOnlyDevice(mContext)) {
+ if (!BatteryStatsHelper.checkWifiOnly(mContext)) {
mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
}
mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
@@ -81,14 +80,6 @@
return mPowerCalculators;
}
- private static boolean isWifiOnlyDevice(Context context) {
- ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
- if (cm == null) {
- return false;
- }
- return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
- }
-
/**
* Returns a snapshot of battery attribution data.
*/
@@ -123,14 +114,9 @@
.setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0))
.setConsumedPower(batteryStatsHelper.getTotalPower());
- final List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
- for (int i = 0; i < usageList.size(); i++) {
- final BatterySipper sipper = usageList.get(i);
- if (sipper.drainType == BatterySipper.DrainType.APP) {
- batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(sipper.uidObj)
- .setPackageWithHighestDrain(sipper.packageWithHighestDrain)
- .setConsumedPower(sipper.sumPower());
- }
+ SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
+ for (int i = uidStats.size() - 1; i >= 0; i--) {
+ batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
}
final long realtimeUs = SystemClock.elapsedRealtime() * 1000;
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
index 974894f..9ea4515 100644
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -162,4 +162,10 @@
// Use English locale because this is never used in UI (only in checkin and dump).
return String.format(Locale.ENGLISH, format, power);
}
+
+ static double mAhToUJ(long energyUJ) {
+ // TODO(b/173765509): Convert properly. This is mJ / V * (h/3600s) = mAh with V = 3.7 fixed.
+ // Leaving for later since desired units of energy have yet to be decided
+ return energyUJ / 1000.0 / 3.7 / 3600;
+ }
}
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index 9c4a267..c86c795 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -24,7 +24,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateUtils;
-import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseLongArray;
@@ -62,7 +62,8 @@
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah);
}
- // TODO(b/178140704): Attribute screen usage similar to smearScreenBatterySipper.
+ // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats.
+ // TODO(b/178140704): Attribute (measured/smeared) usage *per app* for BatteryUsageStats.
}
/**
@@ -71,19 +72,45 @@
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType);
- final double powerMah = computePower(batteryStats, rawRealtimeUs, statsType, durationMs);
- if (powerMah != 0) {
- final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0);
- bs.usagePowerMah = powerMah;
- bs.usageTimeMs = durationMs;
- bs.sumPower();
- sippers.add(bs);
+ final long energyUJ = batteryStats.getScreenOnEnergy();
+ final boolean isMeasuredDataAvailable = energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE;
+
+ final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType);
+ final double powerMah = getMeasuredOrComputedPower(
+ energyUJ, batteryStats, rawRealtimeUs, statsType, durationMs);
+ if (powerMah == 0) {
+ return;
+ }
+
+ // First deal with the SCREEN BatterySipper (since we need this for smearing over apps).
+ final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0);
+ bs.usagePowerMah = powerMah;
+ bs.usageTimeMs = durationMs;
+ bs.sumPower();
+ sippers.add(bs);
+
+ // Now deal with each app's BatterySipper. The results are stored in the screenPowerMah
+ // field, which is considered smeared, but the method depends on the data source.
+ if (isMeasuredDataAvailable) {
+ super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
+ } else {
smearScreenBatterySipper(sippers, bs);
}
}
+ @Override
+ protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ final long energyUJ = u.getScreenOnEnergy();
+ if (energyUJ < 0) {
+ Slog.wtf(TAG, "Screen energy not supported, so calculateApp shouldn't de called");
+ return;
+ }
+ if (energyUJ == 0) return;
+ app.screenPowerMah = mAhToUJ(u.getScreenOnEnergy());
+ }
+
private long computeDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000;
}
@@ -97,7 +124,7 @@
final double binPowerMah = mScreenFullPowerEstimator.calculatePower(brightnessTime)
* (i + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
if (DEBUG && binPowerMah != 0) {
- Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
+ Slog.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
+ " power=" + formatCharge(binPowerMah));
}
power += binPowerMah;
@@ -105,6 +132,16 @@
return power;
}
+ private double getMeasuredOrComputedPower(long measuredEnergyUJ,
+ BatteryStats batteryStats, long rawRealtimeUs, int statsType, long durationMs) {
+
+ if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
+ return mAhToUJ(measuredEnergyUJ);
+ } else {
+ return computePower(batteryStats, rawRealtimeUs, statsType, durationMs);
+ }
+ }
+
/**
* Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
* time, and store this in the {@link BatterySipper#screenPowerMah} field.
@@ -124,10 +161,11 @@
}
if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
- final double screenPowerMah = screenSipper.totalPowerMah;
+ final double totalScreenPowerMah = screenSipper.totalPowerMah;
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper sipper = sippers.get(i);
- sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
+ sipper.screenPowerMah = totalScreenPowerMah
+ * activityTimeArray.get(sipper.getUid(), 0)
/ totalActivityTimeMs;
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fda87be..4b343af 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,13 +116,6 @@
*/
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);
/**
* Enable pointer tagging in this process.
diff --git a/services/core/java/com/android/server/AttributeCache.java b/core/java/com/android/internal/policy/AttributeCache.java
similarity index 84%
rename from services/core/java/com/android/server/AttributeCache.java
rename to core/java/com/android/internal/policy/AttributeCache.java
index 58ec836..1bdad25 100644
--- a/services/core/java/com/android/server/AttributeCache.java
+++ b/core/java/com/android/internal/policy/AttributeCache.java
@@ -1,21 +1,20 @@
/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+package com.android.internal.policy;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -34,6 +33,7 @@
* TODO: This should be better integrated into the system so it doesn't need
* special calls from the activity manager to clear it.
*/
+/** @hide */
public final class AttributeCache {
private static final int CACHE_SIZE = 4;
private static AttributeCache sInstance = null;
@@ -54,11 +54,11 @@
context = c;
}
}
-
+
public final static class Entry {
public final Context context;
public final TypedArray array;
-
+
public Entry(Context c, TypedArray ta) {
context = c;
array = ta;
@@ -70,17 +70,17 @@
}
}
}
-
+
public static void init(Context context) {
if (sInstance == null) {
sInstance = new AttributeCache(context);
}
}
-
+
public static AttributeCache instance() {
return sInstance;
}
-
+
public AttributeCache(Context context) {
mContext = context;
}
@@ -115,7 +115,11 @@
}
}
}
-
+
+ public Entry get(String packageName, int resId, int[] styleable) {
+ return get(packageName, resId, styleable, UserHandle.USER_CURRENT);
+ }
+
public Entry get(String packageName, int resId, int[] styleable, int userId) {
synchronized (this) {
Package pkg = mPackages.get(packageName);
@@ -143,12 +147,12 @@
pkg = new Package(context);
mPackages.put(packageName, pkg);
}
-
+
if (map == null) {
map = new ArrayMap<>();
pkg.mMap.put(resId, map);
}
-
+
try {
ent = new Entry(pkg.context,
pkg.context.obtainStyledAttributes(resId, styleable));
@@ -156,7 +160,7 @@
} catch (Resources.NotFoundException e) {
return null;
}
-
+
return ent;
}
}
diff --git a/services/core/java/com/android/server/wm/animation/ClipRectLRAnimation.java b/core/java/com/android/internal/policy/ClipRectLRAnimation.java
similarity index 95%
rename from services/core/java/com/android/server/wm/animation/ClipRectLRAnimation.java
rename to core/java/com/android/internal/policy/ClipRectLRAnimation.java
index 0db4c70..4dc3cd3 100644
--- a/services/core/java/com/android/server/wm/animation/ClipRectLRAnimation.java
+++ b/core/java/com/android/internal/policy/ClipRectLRAnimation.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.server.wm.animation;
+package com.android.internal.policy;
import android.graphics.Rect;
import android.view.animation.ClipRectAnimation;
diff --git a/services/core/java/com/android/server/wm/animation/ClipRectTBAnimation.java b/core/java/com/android/internal/policy/ClipRectTBAnimation.java
similarity index 96%
rename from services/core/java/com/android/server/wm/animation/ClipRectTBAnimation.java
rename to core/java/com/android/internal/policy/ClipRectTBAnimation.java
index 1f5b1a3..24913cf 100644
--- a/services/core/java/com/android/server/wm/animation/ClipRectTBAnimation.java
+++ b/core/java/com/android/internal/policy/ClipRectTBAnimation.java
@@ -11,16 +11,15 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.server.wm.animation;
+package com.android.internal.policy;
import android.graphics.Rect;
import android.view.animation.ClipRectAnimation;
import android.view.animation.Interpolator;
import android.view.animation.Transformation;
-import android.view.animation.TranslateAnimation;
/**
* Special case of ClipRectAnimation that animates only the top/bottom
diff --git a/services/core/java/com/android/server/policy/LogDecelerateInterpolator.java b/core/java/com/android/internal/policy/LogDecelerateInterpolator.java
similarity index 93%
rename from services/core/java/com/android/server/policy/LogDecelerateInterpolator.java
rename to core/java/com/android/internal/policy/LogDecelerateInterpolator.java
index ed5dc6f..dee77aa 100644
--- a/services/core/java/com/android/server/policy/LogDecelerateInterpolator.java
+++ b/core/java/com/android/internal/policy/LogDecelerateInterpolator.java
@@ -11,13 +11,14 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.server.policy;
+package com.android.internal.policy;
import android.view.animation.Interpolator;
+/** @hide */
public class LogDecelerateInterpolator implements Interpolator {
private int mBase;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 3be841c..5df175e 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2540,15 +2540,16 @@
}
}
- if (a.getBoolean(R.styleable.Window_windowBackgroundBlurEnabled, false)) {
+ if (a.getBoolean(R.styleable.Window_windowBlurBehindEnabled, false)) {
if ((getForcedWindowFlags() & WindowManager.LayoutParams.FLAG_BLUR_BEHIND) == 0) {
params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
}
- params.backgroundBlurRadius = a.getDimensionPixelSize(
- android.R.styleable.Window_windowBackgroundBlurRadius, 0);
+ params.blurBehindRadius = a.getDimensionPixelSize(
+ android.R.styleable.Window_windowBlurBehindRadius, 0);
}
+
if (params.windowAnimations == 0) {
params.windowAnimations = a.getResourceId(
R.styleable.Window_windowAnimationStyle, 0);
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
new file mode 100644
index 0000000..56b25b2
--- /dev/null
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -0,0 +1,960 @@
+/*
+ * 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.internal.policy;
+
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.ResourceId;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.SystemProperties;
+import android.util.Slog;
+import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionOldType;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
+
+import com.android.internal.R;
+
+import java.util.List;
+
+/** @hide */
+public class TransitionAnimation {
+ // These are the possible states for the enter/exit activities during a thumbnail transition
+ public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
+ public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
+ public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
+ public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
+
+ /**
+ * Maximum duration for the clip reveal animation. This is used when there is a lot of movement
+ * involved, to make it more understandable.
+ */
+ private static final int MAX_CLIP_REVEAL_TRANSITION_DURATION = 420;
+ private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8;
+ private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
+
+ public static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+
+ /** Fraction of animation at which the recents thumbnail becomes completely transparent */
+ private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
+
+ private final Context mContext;
+ private final String mTag;
+
+ private final LogDecelerateInterpolator mInterpolator = new LogDecelerateInterpolator(100, 0);
+ /** Interpolator to be used for animations that respond directly to a touch */
+ private final Interpolator mTouchResponseInterpolator =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+ private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
+ private final Interpolator mDecelerateInterpolator;
+ private final Interpolator mLinearOutSlowInInterpolator;
+ private final Interpolator mThumbnailFadeOutInterpolator;
+ private final Rect mTmpFromClipRect = new Rect();
+ private final Rect mTmpToClipRect = new Rect();
+ private final Rect mTmpRect = new Rect();
+
+ private final int mClipRevealTranslationY;
+ private final int mConfigShortAnimTime;
+ private final int mDefaultWindowAnimationStyleResId;
+
+ private final boolean mDebug;
+ private final boolean mGridLayoutRecentsEnabled;
+ private final boolean mLowRamRecentsEnabled;
+
+ public TransitionAnimation(Context context, boolean debug, String tag) {
+ mContext = context;
+ mDebug = debug;
+ mTag = tag;
+
+ mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.decelerate_cubic);
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
+ mThumbnailFadeOutInterpolator = input -> {
+ // Linear response for first fraction, then complete after that.
+ if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
+ float t = input / RECENTS_THUMBNAIL_FADEOUT_FRACTION;
+ return mLinearOutSlowInInterpolator.getInterpolation(t);
+ }
+ return 1f;
+ };
+
+ mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP
+ * mContext.getResources().getDisplayMetrics().density);
+ mConfigShortAnimTime = context.getResources().getInteger(
+ com.android.internal.R.integer.config_shortAnimTime);
+
+ mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
+ mLowRamRecentsEnabled = ActivityManager.isLowRamDeviceStatic();
+
+ final TypedArray windowStyle = mContext.getTheme().obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ mDefaultWindowAnimationStyleResId = windowStyle.getResourceId(
+ com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+ windowStyle.recycle();
+ }
+
+ public Animation loadKeyguardExitAnimation(int transit, int transitionFlags) {
+ if ((transitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) != 0) {
+ return null;
+ }
+ final boolean toShade =
+ (transitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0;
+ final boolean subtle =
+ (transitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0;
+ return createHiddenByKeyguardExit(mContext, mInterpolator,
+ transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER, toShade, subtle);
+ }
+
+ @Nullable
+ public Animation loadKeyguardUnoccludeAnimation(LayoutParams lp) {
+ return loadAnimationRes(lp, com.android.internal.R.anim.wallpaper_open_exit);
+ }
+
+ @Nullable
+ public Animation loadVoiceActivityOpenAnimation(LayoutParams lp, boolean enter) {
+ return loadAnimationRes(lp, enter
+ ? com.android.internal.R.anim.voice_activity_open_enter
+ : com.android.internal.R.anim.voice_activity_open_exit);
+ }
+
+ @Nullable
+ public Animation loadVoiceActivityExitAnimation(LayoutParams lp, boolean enter) {
+ return loadAnimationRes(lp, enter
+ ? com.android.internal.R.anim.voice_activity_close_enter
+ : com.android.internal.R.anim.voice_activity_close_exit);
+ }
+
+ @Nullable
+ public Animation loadAppTransitionAnimation(String packageName, int resId) {
+ return loadAnimationRes(packageName, resId);
+ }
+
+ @Nullable
+ public Animation loadCrossProfileAppEnterAnimation() {
+ return loadAnimationRes("android",
+ com.android.internal.R.anim.task_open_enter_cross_profile_apps);
+ }
+
+ @Nullable
+ public Animation loadCrossProfileAppThumbnailEnterAnimation() {
+ return loadAnimationRes(
+ "android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
+ }
+
+ @Nullable
+ private Animation loadAnimationRes(LayoutParams lp, int resId) {
+ Context context = mContext;
+ if (ResourceId.isValid(resId)) {
+ AttributeCache.Entry ent = getCachedAnimations(lp);
+ if (ent != null) {
+ context = ent.context;
+ }
+ return loadAnimationSafely(context, resId, mTag);
+ }
+ return null;
+ }
+
+ @Nullable
+ private Animation loadAnimationRes(String packageName, int resId) {
+ if (ResourceId.isValid(resId)) {
+ AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
+ if (ent != null) {
+ return loadAnimationSafely(ent.context, resId, mTag);
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
+ int resId = Resources.ID_NULL;
+ Context context = mContext;
+ if (animAttr >= 0) {
+ AttributeCache.Entry ent = getCachedAnimations(lp);
+ if (ent != null) {
+ context = ent.context;
+ resId = ent.array.getResourceId(animAttr, 0);
+ }
+ }
+ resId = updateToTranslucentAnimIfNeeded(resId, transit);
+ if (ResourceId.isValid(resId)) {
+ return loadAnimationSafely(context, resId, mTag);
+ }
+ return null;
+ }
+
+ @Nullable
+ private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
+ if (mDebug) {
+ Slog.v(mTag, "Loading animations: layout params pkg="
+ + (lp != null ? lp.packageName : null)
+ + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
+ }
+ if (lp != null && lp.windowAnimations != 0) {
+ // If this is a system resource, don't try to load it from the
+ // application resources. It is nice to avoid loading application
+ // resources if we can.
+ String packageName = lp.packageName != null ? lp.packageName : "android";
+ int resId = getAnimationStyleResId(lp);
+ if ((resId & 0xFF000000) == 0x01000000) {
+ packageName = "android";
+ }
+ if (mDebug) {
+ Slog.v(mTag, "Loading animations: picked package=" + packageName);
+ }
+ return AttributeCache.instance().get(packageName, resId,
+ com.android.internal.R.styleable.WindowAnimation);
+ }
+ return null;
+ }
+
+ @Nullable
+ private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
+ if (mDebug) {
+ Slog.v(mTag, "Loading animations: package="
+ + packageName + " resId=0x" + Integer.toHexString(resId));
+ }
+ if (packageName != null) {
+ if ((resId & 0xFF000000) == 0x01000000) {
+ packageName = "android";
+ }
+ if (mDebug) {
+ Slog.v(mTag, "Loading animations: picked package="
+ + packageName);
+ }
+ return AttributeCache.instance().get(packageName, resId,
+ com.android.internal.R.styleable.WindowAnimation);
+ }
+ return null;
+ }
+
+ /** Returns window animation style ID from {@link LayoutParams} or from system in some cases */
+ public int getAnimationStyleResId(@NonNull LayoutParams lp) {
+ int resId = lp.windowAnimations;
+ if (lp.type == LayoutParams.TYPE_APPLICATION_STARTING) {
+ // Note that we don't want application to customize starting window animation.
+ // Since this window is specific for displaying while app starting,
+ // application should not change its animation directly.
+ // In this case, it will use system resource to get default animation.
+ resId = mDefaultWindowAnimationStyleResId;
+ }
+ return resId;
+ }
+
+ public Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets,
+ Rect startRect) {
+ setupDefaultNextAppTransitionStartRect(startRect, mTmpFromClipRect);
+ final int left = mTmpFromClipRect.left;
+ final int top = mTmpFromClipRect.top;
+ mTmpFromClipRect.offset(-left, -top);
+ // TODO: Isn't that strange that we ignore exact position of the containingFrame?
+ mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height());
+ AnimationSet set = new AnimationSet(true);
+ float fromWidth = mTmpFromClipRect.width();
+ float toWidth = mTmpToClipRect.width();
+ float fromHeight = mTmpFromClipRect.height();
+ // While the window might span the whole display, the actual content will be cropped to the
+ // system decoration frame, for example when the window is docked. We need to take into
+ // account the visible height when constructing the animation.
+ float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom;
+ int translateAdjustment = 0;
+ if (fromWidth <= toWidth && fromHeight <= toHeight) {
+ // The final window is larger in both dimensions than current window (e.g. we are
+ // maximizing), so we can simply unclip the new window and there will be no disappearing
+ // frame.
+ set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect));
+ } else {
+ // The disappearing window has one larger dimension. We need to apply scaling, so the
+ // first frame of the entry animation matches the old window.
+ set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
+ // We might not be going exactly full screen, but instead be aligned under the status
+ // bar using cropping. We still need to account for the cropped part, which will also
+ // be scaled.
+ translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight);
+ }
+
+ // We animate the translation from the old position of the removed window, to the new
+ // position of the added window. The latter might not be full screen, for example docked for
+ // docked windows.
+ TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
+ 0, top - containingFrame.top - translateAdjustment, 0);
+ set.addAnimation(translate);
+ set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+ set.setZAdjustment(Animation.ZORDER_TOP);
+ return set;
+ }
+
+ private void setupDefaultNextAppTransitionStartRect(Rect startRect, Rect rect) {
+ if (startRect == null) {
+ Slog.e(mTag, "Starting rect for app requested, but none available", new Throwable());
+ rect.setEmpty();
+ } else {
+ rect.set(startRect);
+ }
+ }
+
+ public Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
+ Rect displayFrame, Rect startRect) {
+ final Animation anim;
+ if (enter) {
+ final int appWidth = appFrame.width();
+ final int appHeight = appFrame.height();
+
+ // mTmpRect will contain an area around the launcher icon that was pressed. We will
+ // clip reveal from that area in the final area of the app.
+ setupDefaultNextAppTransitionStartRect(startRect, mTmpRect);
+
+ float t = 0f;
+ if (appHeight > 0) {
+ t = (float) mTmpRect.top / displayFrame.height();
+ }
+ int translationY = mClipRevealTranslationY + (int) (displayFrame.height() / 7f * t);
+ int translationX = 0;
+ int translationYCorrection = translationY;
+ int centerX = mTmpRect.centerX();
+ int centerY = mTmpRect.centerY();
+ int halfWidth = mTmpRect.width() / 2;
+ int halfHeight = mTmpRect.height() / 2;
+ int clipStartX = centerX - halfWidth - appFrame.left;
+ int clipStartY = centerY - halfHeight - appFrame.top;
+ boolean cutOff = false;
+
+ // If the starting rectangle is fully or partially outside of the target rectangle, we
+ // need to start the clipping at the edge and then achieve the rest with translation
+ // and extending the clip rect from that edge.
+ if (appFrame.top > centerY - halfHeight) {
+ translationY = (centerY - halfHeight) - appFrame.top;
+ translationYCorrection = 0;
+ clipStartY = 0;
+ cutOff = true;
+ }
+ if (appFrame.left > centerX - halfWidth) {
+ translationX = (centerX - halfWidth) - appFrame.left;
+ clipStartX = 0;
+ cutOff = true;
+ }
+ if (appFrame.right < centerX + halfWidth) {
+ translationX = (centerX + halfWidth) - appFrame.right;
+ clipStartX = appWidth - mTmpRect.width();
+ cutOff = true;
+ }
+ final long duration = calculateClipRevealTransitionDuration(cutOff, translationX,
+ translationY, displayFrame);
+
+ // Clip third of the from size of launch icon, expand to full width/height
+ Animation clipAnimLR = new ClipRectLRAnimation(
+ clipStartX, clipStartX + mTmpRect.width(), 0, appWidth);
+ clipAnimLR.setInterpolator(mClipHorizontalInterpolator);
+ clipAnimLR.setDuration((long) (duration / 2.5f));
+
+ TranslateAnimation translate = new TranslateAnimation(translationX, 0, translationY, 0);
+ translate.setInterpolator(cutOff ? mTouchResponseInterpolator
+ : mLinearOutSlowInInterpolator);
+ translate.setDuration(duration);
+
+ Animation clipAnimTB = new ClipRectTBAnimation(
+ clipStartY, clipStartY + mTmpRect.height(),
+ 0, appHeight,
+ translationYCorrection, 0,
+ mLinearOutSlowInInterpolator);
+ clipAnimTB.setInterpolator(mTouchResponseInterpolator);
+ clipAnimTB.setDuration(duration);
+
+ // Quick fade-in from icon to app window
+ final long alphaDuration = duration / 4;
+ AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
+ alpha.setDuration(alphaDuration);
+ alpha.setInterpolator(mLinearOutSlowInInterpolator);
+
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(clipAnimLR);
+ set.addAnimation(clipAnimTB);
+ set.addAnimation(translate);
+ set.addAnimation(alpha);
+ set.setZAdjustment(Animation.ZORDER_TOP);
+ set.initialize(appWidth, appHeight, appWidth, appHeight);
+ anim = set;
+ } else {
+ final long duration;
+ switch (transit) {
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
+ duration = mConfigShortAnimTime;
+ break;
+ default:
+ duration = DEFAULT_APP_TRANSITION_DURATION;
+ break;
+ }
+ if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN
+ || transit == TRANSIT_OLD_WALLPAPER_INTRA_CLOSE) {
+ // If we are on top of the wallpaper, we need an animation that
+ // correctly handles the wallpaper staying static behind all of
+ // the animated elements. To do this, will just have the existing
+ // element fade out.
+ anim = new AlphaAnimation(1, 0);
+ anim.setDetachWallpaper(true);
+ } else {
+ // For normal animations, the exiting element just holds in place.
+ anim = new AlphaAnimation(1, 1);
+ }
+ anim.setInterpolator(mDecelerateInterpolator);
+ anim.setDuration(duration);
+ anim.setFillAfter(true);
+ }
+ return anim;
+ }
+
+ public Animation createScaleUpAnimationLocked(int transit, boolean enter,
+ Rect containingFrame, Rect startRect) {
+ Animation a;
+ setupDefaultNextAppTransitionStartRect(startRect, mTmpRect);
+ final int appWidth = containingFrame.width();
+ final int appHeight = containingFrame.height();
+ if (enter) {
+ // Entering app zooms out from the center of the initial rect.
+ float scaleW = mTmpRect.width() / (float) appWidth;
+ float scaleH = mTmpRect.height() / (float) appHeight;
+ Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
+ computePivot(mTmpRect.left, scaleW),
+ computePivot(mTmpRect.top, scaleH));
+ scale.setInterpolator(mDecelerateInterpolator);
+
+ Animation alpha = new AlphaAnimation(0, 1);
+ alpha.setInterpolator(mThumbnailFadeOutInterpolator);
+
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(scale);
+ set.addAnimation(alpha);
+ set.setDetachWallpaper(true);
+ a = set;
+ } else if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN
+ || transit == TRANSIT_OLD_WALLPAPER_INTRA_CLOSE) {
+ // If we are on top of the wallpaper, we need an animation that
+ // correctly handles the wallpaper staying static behind all of
+ // the animated elements. To do this, will just have the existing
+ // element fade out.
+ a = new AlphaAnimation(1, 0);
+ a.setDetachWallpaper(true);
+ } else {
+ // For normal animations, the exiting element just holds in place.
+ a = new AlphaAnimation(1, 1);
+ }
+
+ // Pick the desired duration. If this is an inter-activity transition,
+ // it is the standard duration for that. Otherwise we use the longer
+ // task transition duration.
+ final long duration;
+ switch (transit) {
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
+ duration = mConfigShortAnimTime;
+ break;
+ default:
+ duration = DEFAULT_APP_TRANSITION_DURATION;
+ break;
+ }
+ a.setDuration(duration);
+ a.setFillAfter(true);
+ a.setInterpolator(mDecelerateInterpolator);
+ a.initialize(appWidth, appHeight, appWidth, appHeight);
+ return a;
+ }
+
+ /**
+ * This animation is created when we are doing a thumbnail transition, for the activity that is
+ * leaving, and the activity that is entering.
+ */
+ public Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState,
+ Rect containingFrame, int transit, HardwareBuffer thumbnailHeader,
+ Rect startRect) {
+ final int appWidth = containingFrame.width();
+ final int appHeight = containingFrame.height();
+ Animation a;
+ setupDefaultNextAppTransitionStartRect(startRect, mTmpRect);
+ final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
+ final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+ switch (thumbTransitState) {
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
+ // Entering app scales up with the thumbnail
+ float scaleW = thumbWidth / appWidth;
+ float scaleH = thumbHeight / appHeight;
+ a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+ computePivot(mTmpRect.left, scaleW),
+ computePivot(mTmpRect.top, scaleH));
+ break;
+ }
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+ // Exiting app while the thumbnail is scaling up should fade or stay in place
+ if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN) {
+ // Fade out while bringing up selected activity. This keeps the
+ // current activity from showing through a launching wallpaper
+ // activity.
+ a = new AlphaAnimation(1, 0);
+ } else {
+ // noop animation
+ a = new AlphaAnimation(1, 1);
+ }
+ break;
+ }
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+ // Entering the other app, it should just be visible while we scale the thumbnail
+ // down above it
+ a = new AlphaAnimation(1, 1);
+ break;
+ }
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+ // Exiting the current app, the app should scale down with the thumbnail
+ float scaleW = thumbWidth / appWidth;
+ float scaleH = thumbHeight / appHeight;
+ Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+ computePivot(mTmpRect.left, scaleW),
+ computePivot(mTmpRect.top, scaleH));
+
+ Animation alpha = new AlphaAnimation(1, 0);
+
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(scale);
+ set.addAnimation(alpha);
+ set.setZAdjustment(Animation.ZORDER_TOP);
+ a = set;
+ break;
+ }
+ default:
+ throw new RuntimeException("Invalid thumbnail transition state");
+ }
+
+ return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+ }
+
+ /**
+ * This alternate animation is created when we are doing a thumbnail transition, for the
+ * activity that is leaving, and the activity that is entering.
+ */
+ public Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
+ int orientation, int transit, Rect containingFrame, Rect contentInsets,
+ @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform,
+ Rect startRect, Rect defaultStartRect) {
+ Animation a;
+ final int appWidth = containingFrame.width();
+ final int appHeight = containingFrame.height();
+ setupDefaultNextAppTransitionStartRect(defaultStartRect, mTmpRect);
+ final int thumbWidthI = mTmpRect.width();
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = mTmpRect.height();
+ final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+ final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left;
+ final int thumbStartY = mTmpRect.top - containingFrame.top;
+
+ switch (thumbTransitState) {
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+ final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+ if (freeform && scaleUp) {
+ a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
+ containingFrame, surfaceInsets, startRect, defaultStartRect);
+ } else if (freeform) {
+ a = createAspectScaledThumbnailExitFreeformAnimationLocked(
+ containingFrame, surfaceInsets, startRect, defaultStartRect);
+ } else {
+ AnimationSet set = new AnimationSet(true);
+
+ // In portrait, we scale to fit the width
+ mTmpFromClipRect.set(containingFrame);
+ mTmpToClipRect.set(containingFrame);
+
+ // Containing frame is in screen space, but we need the clip rect in the
+ // app space.
+ mTmpFromClipRect.offsetTo(0, 0);
+ mTmpToClipRect.offsetTo(0, 0);
+
+ // Exclude insets region from the source clip.
+ mTmpFromClipRect.inset(contentInsets);
+
+ if (shouldScaleDownThumbnailTransition(orientation)) {
+ // We scale the width and clip to the top/left square
+ float scale =
+ thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
+ if (!mGridLayoutRecentsEnabled) {
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
+ }
+
+ Animation scaleAnim = new ScaleAnimation(
+ scaleUp ? scale : 1, scaleUp ? 1 : scale,
+ scaleUp ? scale : 1, scaleUp ? 1 : scale,
+ containingFrame.width() / 2f,
+ containingFrame.height() / 2f + contentInsets.top);
+ final float targetX = (mTmpRect.left - containingFrame.left);
+ final float x = containingFrame.width() / 2f
+ - containingFrame.width() / 2f * scale;
+ final float targetY = (mTmpRect.top - containingFrame.top);
+ float y = containingFrame.height() / 2f
+ - containingFrame.height() / 2f * scale;
+
+ // During transition may require clipping offset from any top stable insets
+ // such as the statusbar height when statusbar is hidden
+ if (mLowRamRecentsEnabled && contentInsets.top == 0 && scaleUp) {
+ mTmpFromClipRect.top += stableInsets.top;
+ y += stableInsets.top;
+ }
+ final float startX = targetX - x;
+ final float startY = targetY - y;
+ Animation clipAnim = scaleUp
+ ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
+ : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
+ Animation translateAnim = scaleUp
+ ? createCurvedMotion(startX, 0, startY - contentInsets.top, 0)
+ : createCurvedMotion(0, startX, 0, startY - contentInsets.top);
+
+ set.addAnimation(clipAnim);
+ set.addAnimation(scaleAnim);
+ set.addAnimation(translateAnim);
+
+ } else {
+ // In landscape, we don't scale at all and only crop
+ mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI;
+ mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI;
+
+ Animation clipAnim = scaleUp
+ ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
+ : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
+ Animation translateAnim = scaleUp
+ ? createCurvedMotion(thumbStartX, 0,
+ thumbStartY - contentInsets.top, 0)
+ : createCurvedMotion(0, thumbStartX, 0,
+ thumbStartY - contentInsets.top);
+
+ set.addAnimation(clipAnim);
+ set.addAnimation(translateAnim);
+ }
+ a = set;
+ a.setZAdjustment(Animation.ZORDER_TOP);
+ }
+ break;
+ }
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+ // Previous app window during the scale up
+ if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN) {
+ // Fade out the source activity if we are animating to a wallpaper
+ // activity.
+ a = new AlphaAnimation(1, 0);
+ } else {
+ a = new AlphaAnimation(1, 1);
+ }
+ break;
+ }
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+ // Target app window during the scale down
+ if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN) {
+ // Fade in the destination activity if we are animating from a wallpaper
+ // activity.
+ a = new AlphaAnimation(0, 1);
+ } else {
+ a = new AlphaAnimation(1, 1);
+ }
+ break;
+ }
+ default:
+ throw new RuntimeException("Invalid thumbnail transition state");
+ }
+
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
+ THUMBNAIL_APP_TRANSITION_DURATION, mTouchResponseInterpolator);
+ }
+
+ /**
+ * Prepares the specified animation with a standard duration, interpolator, etc.
+ */
+ private Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight,
+ int transit) {
+ // Pick the desired duration. If this is an inter-activity transition,
+ // it is the standard duration for that. Otherwise we use the longer
+ // task transition duration.
+ final int duration;
+ switch (transit) {
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
+ duration = mConfigShortAnimTime;
+ break;
+ default:
+ duration = DEFAULT_APP_TRANSITION_DURATION;
+ break;
+ }
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
+ mDecelerateInterpolator);
+ }
+
+
+ private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
+ @Nullable Rect surfaceInsets, @Nullable Rect startRect,
+ @Nullable Rect defaultStartRect) {
+ getNextAppTransitionStartRect(startRect, defaultStartRect, mTmpRect);
+ return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets,
+ true);
+ }
+
+ private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame,
+ @Nullable Rect surfaceInsets, @Nullable Rect startRect,
+ @Nullable Rect defaultStartRect) {
+ getNextAppTransitionStartRect(startRect, defaultStartRect, mTmpRect);
+ return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets,
+ false);
+ }
+
+ private void getNextAppTransitionStartRect(Rect startRect, Rect defaultStartRect, Rect rect) {
+ if (startRect == null && defaultStartRect == null) {
+ Slog.e(mTag, "Starting rect for container not available", new Throwable());
+ rect.setEmpty();
+ } else {
+ rect.set(startRect != null ? startRect : defaultStartRect);
+ }
+ }
+
+ private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame,
+ Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) {
+ final float sourceWidth = sourceFrame.width();
+ final float sourceHeight = sourceFrame.height();
+ final float destWidth = destFrame.width();
+ final float destHeight = destFrame.height();
+ final float scaleH = enter ? sourceWidth / destWidth : destWidth / sourceWidth;
+ final float scaleV = enter ? sourceHeight / destHeight : destHeight / sourceHeight;
+ AnimationSet set = new AnimationSet(true);
+ final int surfaceInsetsH = surfaceInsets == null
+ ? 0 : surfaceInsets.left + surfaceInsets.right;
+ final int surfaceInsetsV = surfaceInsets == null
+ ? 0 : surfaceInsets.top + surfaceInsets.bottom;
+ // We want the scaling to happen from the center of the surface. In order to achieve that,
+ // we need to account for surface insets that will be used to enlarge the surface.
+ final float scaleHCenter = ((enter ? destWidth : sourceWidth) + surfaceInsetsH) / 2;
+ final float scaleVCenter = ((enter ? destHeight : sourceHeight) + surfaceInsetsV) / 2;
+ final ScaleAnimation scale = enter
+ ? new ScaleAnimation(scaleH, 1, scaleV, 1, scaleHCenter, scaleVCenter)
+ : new ScaleAnimation(1, scaleH, 1, scaleV, scaleHCenter, scaleVCenter);
+ final int sourceHCenter = sourceFrame.left + sourceFrame.width() / 2;
+ final int sourceVCenter = sourceFrame.top + sourceFrame.height() / 2;
+ final int destHCenter = destFrame.left + destFrame.width() / 2;
+ final int destVCenter = destFrame.top + destFrame.height() / 2;
+ final int fromX = enter ? sourceHCenter - destHCenter : destHCenter - sourceHCenter;
+ final int fromY = enter ? sourceVCenter - destVCenter : destVCenter - sourceVCenter;
+ final TranslateAnimation translation = enter ? new TranslateAnimation(fromX, 0, fromY, 0)
+ : new TranslateAnimation(0, fromX, 0, fromY);
+ set.addAnimation(scale);
+ set.addAnimation(translation);
+ return set;
+ }
+
+ /**
+ * @return whether the transition should show the thumbnail being scaled down.
+ */
+ private boolean shouldScaleDownThumbnailTransition(int orientation) {
+ return mGridLayoutRecentsEnabled
+ || orientation == Configuration.ORIENTATION_PORTRAIT;
+ }
+
+ private static int updateToTranslucentAnimIfNeeded(int anim, @TransitionOldType int transit) {
+ if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN
+ && anim == R.anim.activity_open_enter) {
+ return R.anim.activity_translucent_open_enter;
+ }
+ if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE
+ && anim == R.anim.activity_close_exit) {
+ return R.anim.activity_translucent_close_exit;
+ }
+ return anim;
+ }
+
+ /**
+ * Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
+ * the start rect is outside of the target rect, and there is a lot of movement going on.
+ *
+ * @param cutOff whether the start rect was not fully contained by the end rect
+ * @param translationX the total translation the surface moves in x direction
+ * @param translationY the total translation the surfaces moves in y direction
+ * @param displayFrame our display frame
+ *
+ * @return the duration of the clip reveal animation, in milliseconds
+ */
+ private static long calculateClipRevealTransitionDuration(boolean cutOff, float translationX,
+ float translationY, Rect displayFrame) {
+ if (!cutOff) {
+ return DEFAULT_APP_TRANSITION_DURATION;
+ }
+ final float fraction = Math.max(Math.abs(translationX) / displayFrame.width(),
+ Math.abs(translationY) / displayFrame.height());
+ return (long) (DEFAULT_APP_TRANSITION_DURATION + fraction
+ * (MAX_CLIP_REVEAL_TRANSITION_DURATION - DEFAULT_APP_TRANSITION_DURATION));
+ }
+
+ /**
+ * Prepares the specified animation with a standard duration, interpolator, etc.
+ */
+ private static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
+ int appHeight, long duration, Interpolator interpolator) {
+ if (duration > 0) {
+ a.setDuration(duration);
+ }
+ a.setFillAfter(true);
+ if (interpolator != null) {
+ a.setInterpolator(interpolator);
+ }
+ a.initialize(appWidth, appHeight, appWidth, appHeight);
+ return a;
+ }
+
+ private static Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
+ return new TranslateAnimation(fromX, toX, fromY, toY);
+ }
+
+ /**
+ * Compute the pivot point for an animation that is scaling from a small
+ * rect on screen to a larger rect. The pivot point varies depending on
+ * the distance between the inner and outer edges on both sides. This
+ * function computes the pivot point for one dimension.
+ * @param startPos Offset from left/top edge of outer rectangle to
+ * left/top edge of inner rectangle.
+ * @param finalScale The scaling factor between the size of the outer
+ * and inner rectangles.
+ */
+ public static float computePivot(int startPos, float finalScale) {
+
+ /*
+ Theorem of intercepting lines:
+
+ + + +-----------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ x | y | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | + | +--------------------+ |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | +--------------------+ |
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ | +-----------------------------------------------+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ + ++
+ p ++
+
+ scale = (x - y) / x
+ <=> x = -y / (scale - 1)
+ */
+ final float denom = finalScale - 1;
+ if (Math.abs(denom) < .0001f) {
+ return startPos;
+ }
+ return -startPos / denom;
+ }
+
+ @Nullable
+ public static Animation loadAnimationSafely(Context context, int resId, String tag) {
+ try {
+ return AnimationUtils.loadAnimation(context, resId);
+ } catch (Resources.NotFoundException e) {
+ Slog.w(tag, "Unable to load animation resource", e);
+ return null;
+ }
+ }
+
+ public static Animation createHiddenByKeyguardExit(Context context,
+ LogDecelerateInterpolator interpolator, boolean onWallpaper,
+ boolean goingToNotificationShade, boolean subtleAnimation) {
+ if (goingToNotificationShade) {
+ return AnimationUtils.loadAnimation(context, R.anim.lock_screen_behind_enter_fade_in);
+ }
+
+ final int resource;
+ if (subtleAnimation) {
+ resource = R.anim.lock_screen_behind_enter_subtle;
+ } else if (onWallpaper) {
+ resource = R.anim.lock_screen_behind_enter_wallpaper;
+ } else {
+ resource = R.anim.lock_screen_behind_enter;
+ }
+
+ AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(context, resource);
+
+ // TODO: Use XML interpolators when we have log interpolators available in XML.
+ final List<Animation> animations = set.getAnimations();
+ for (int i = animations.size() - 1; i >= 0; --i) {
+ animations.get(i).setInterpolator(interpolator);
+ }
+
+ return set;
+ }
+}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index b744a5d..e310f8d 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -20,8 +20,10 @@
import static android.os.BatteryStats.ENERGY_DATA_UNAVAILABLE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
+import android.util.DebugUtils;
import android.util.Slog;
import android.view.Display;
@@ -33,7 +35,9 @@
import java.lang.annotation.RetentionPolicy;
/**
- * Tracks the measured energy usage of various subsystems according to their {@link EnergyBucket}.
+ * Tracks the measured energy usage of various subsystems according to their
+ * {@link StandardEnergyBucket} or custom energy bucket (which is tied to
+ * {@link android.hardware.power.stats.EnergyConsumer.ordinal}).
*
* This class doesn't use a TimeBase, and instead requires manually decisions about when to
* accumulate since it is trivial. However, in the future, a TimeBase could be used instead.
@@ -42,15 +46,13 @@
public class MeasuredEnergyStats {
private static final String TAG = "MeasuredEnergyStats";
- // Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if energy
- // bucket integers are modified.
+ // Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} MUST be updated if standard
+ // energy bucket integers are modified/added/removed.
public static final int ENERGY_BUCKET_UNKNOWN = -1;
public static final int ENERGY_BUCKET_SCREEN_ON = 0;
public static final int ENERGY_BUCKET_SCREEN_DOZE = 1;
public static final int ENERGY_BUCKET_SCREEN_OTHER = 2;
- public static final int NUMBER_ENERGY_BUCKETS = 3;
- private static final String[] ENERGY_BUCKET_NAMES =
- {"screen-on", "screen-doze", "screen-other"};
+ public static final int NUMBER_STANDARD_ENERGY_BUCKETS = 3; // Buckets above this are custom.
@IntDef(prefix = {"ENERGY_BUCKET_"}, value = {
ENERGY_BUCKET_UNKNOWN,
@@ -59,28 +61,37 @@
ENERGY_BUCKET_SCREEN_OTHER,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface EnergyBucket {
+ public @interface StandardEnergyBucket {
}
/**
- * Total energy (in microjoules) that an {@link EnergyBucket} has accumulated since the last
- * reset. Values MUST be non-zero or ENERGY_DATA_UNAVAILABLE. Accumulation only occurs
+ * Total energy (in microjoules) that an energy bucket (including both
+ * {@link StandardEnergyBucket} and custom buckets) has accumulated since the last reset.
+ * Values MUST be non-zero or ENERGY_DATA_UNAVAILABLE. Accumulation only occurs
* while the necessary conditions are satisfied (e.g. on battery).
*
+ * Energy for both {@link StandardEnergyBucket}s and custom energy buckets are stored in this
+ * array, and may internally both referred to as 'buckets'. This is an implementation detail;
+ * externally, we differentiate between these two data sources.
+ *
* Warning: Long array is used for access speed. If the number of supported subsystems
* becomes large, consider using an alternate data structure such as a SparseLongArray.
*/
- private final long[] mAccumulatedEnergiesMicroJoules = new long[NUMBER_ENERGY_BUCKETS];
+ private final long[] mAccumulatedEnergiesMicroJoules;
/**
* Creates a MeasuredEnergyStats set to support the provided energy buckets.
- * supportedEnergyBuckets should generally be of size {@link #NUMBER_ENERGY_BUCKETS}.
+ * supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_ENERGY_BUCKETS}.
+ * numCustomBuckets >= 0 is the number of (non-standard) custom energy buckets on the device.
*/
- public MeasuredEnergyStats(boolean[] supportedEnergyBuckets) {
+ public MeasuredEnergyStats(boolean[] supportedStandardBuckets, int numCustomBuckets) {
+ final int numTotalBuckets = NUMBER_STANDARD_ENERGY_BUCKETS + numCustomBuckets;
+ mAccumulatedEnergiesMicroJoules = new long[numTotalBuckets];
// Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE.
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- if (!supportedEnergyBuckets[bucket]) {
- mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE;
+ // All custom buckets are, by definition, supported, so their values stay at 0.
+ for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
+ if (!supportedStandardBuckets[stdBucket]) {
+ mAccumulatedEnergiesMicroJoules[stdBucket] = ENERGY_DATA_UNAVAILABLE;
}
}
}
@@ -90,10 +101,13 @@
* supported. This certainly does NOT produce an exact clone of the template.
*/
private MeasuredEnergyStats(MeasuredEnergyStats template) {
+ final int numIndices = template.getNumberOfIndices();
+ mAccumulatedEnergiesMicroJoules = new long[numIndices];
// Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE.
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- if (!template.isEnergyBucketSupported(bucket)) {
- mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE;
+ // All custom buckets are, by definition, supported, so their values stay at 0.
+ for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
+ if (!template.isIndexSupported(stdBucket)) {
+ mAccumulatedEnergiesMicroJoules[stdBucket] = ENERGY_DATA_UNAVAILABLE;
}
}
}
@@ -108,18 +122,22 @@
/**
* Constructor for creating a temp MeasuredEnergyStats.
- * See {@link #readSummaryFromParcel(MeasuredEnergyStats, Parcel)}.
+ * See {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}.
*/
- private MeasuredEnergyStats() {
+ private MeasuredEnergyStats(int numIndices) {
+ mAccumulatedEnergiesMicroJoules = new long[numIndices];
}
/** Construct from parcel. */
public MeasuredEnergyStats(Parcel in) {
+ final int size = in.readInt();
+ mAccumulatedEnergiesMicroJoules = new long[size];
in.readLongArray(mAccumulatedEnergiesMicroJoules);
}
/** Write to parcel */
public void writeToParcel(Parcel out) {
+ out.writeInt(mAccumulatedEnergiesMicroJoules.length);
out.writeLongArray(mAccumulatedEnergiesMicroJoules);
}
@@ -129,16 +147,18 @@
* summary parcel was written. Availability has already been correctly set in the constructor.
* Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary
* parceling changes.
+ *
+ * Corresponding write performed by {@link #writeSummaryToParcel(Parcel, boolean)}.
*/
private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) {
- final int size = in.readInt();
- for (int i = 0; i < size; i++) {
- final int bucket = in.readInt();
+ final int numWrittenEntries = in.readInt();
+ for (int entry = 0; entry < numWrittenEntries; entry++) {
+ final int index = in.readInt();
final long energyUJ = in.readLong();
if (overwriteAvailability) {
- mAccumulatedEnergiesMicroJoules[bucket] = energyUJ;
+ mAccumulatedEnergiesMicroJoules[index] = energyUJ;
} else {
- setValueIfSupported(bucket, energyUJ);
+ setValueIfSupported(index, energyUJ);
}
}
}
@@ -146,52 +166,90 @@
/**
* Write to summary parcel.
* Note: Measured subsystem availability may be different when the summary parcel is read.
+ *
+ * Corresponding read performed by {@link #readSummaryFromParcel(Parcel, boolean)}.
*/
private void writeSummaryToParcel(Parcel out, boolean skipZero) {
- final int sizePos = out.dataPosition();
+ final int posOfNumWrittenEntries = out.dataPosition();
out.writeInt(0);
- int size = 0;
- // Write only the supported buckets with non-zero energy.
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- final long energy = mAccumulatedEnergiesMicroJoules[i];
+ int numWrittenEntries = 0;
+ // Write only the supported buckets (with non-zero energy, if applicable).
+ for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
+ final long energy = mAccumulatedEnergiesMicroJoules[index];
if (energy < 0) continue;
if (energy == 0 && skipZero) continue;
- out.writeInt(i);
- out.writeLong(mAccumulatedEnergiesMicroJoules[i]);
- size++;
+ out.writeInt(index);
+ out.writeLong(mAccumulatedEnergiesMicroJoules[index]);
+ numWrittenEntries++;
}
final int currPos = out.dataPosition();
- out.setDataPosition(sizePos);
- out.writeInt(size);
+ out.setDataPosition(posOfNumWrittenEntries);
+ out.writeInt(numWrittenEntries);
out.setDataPosition(currPos);
}
- /** Updates the given bucket with the given energy iff accumulate is true. */
- public void updateBucket(@EnergyBucket int bucket, long energyDeltaUJ, boolean accumulate) {
+ /** Get number of possible buckets, including both standard and custom ones. */
+ private int getNumberOfIndices() {
+ return mAccumulatedEnergiesMicroJoules.length;
+ }
+
+ // TODO: Get rid of the 'accumulate' boolean. It's always true.
+ /** Updates the given standard energy bucket with the given energy if accumulate is true. */
+ public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ,
+ boolean accumulate) {
+ checkValidStandardBucket(bucket);
+ updateEntry(bucket, energyDeltaUJ, accumulate);
+ }
+
+ /** Updates the given custom energy bucket with the given energy if accumulate is true. */
+ public void updateCustomBucket(int customBucket, long energyDeltaUJ, boolean accumulate) {
+ if (!isValidCustomBucket(customBucket)) {
+ Slog.e(TAG, "Attempted to update invalid custom bucket " + customBucket);
+ return;
+ }
+ final int index = customBucketToIndex(customBucket);
+ updateEntry(index, energyDeltaUJ, accumulate);
+ }
+
+ /** Updates the given index with the given energy if accumulate is true. */
+ private void updateEntry(int index, long energyDeltaUJ, boolean accumulate) {
if (accumulate) {
- if (mAccumulatedEnergiesMicroJoules[bucket] >= 0L) {
- mAccumulatedEnergiesMicroJoules[bucket] += energyDeltaUJ;
+ if (mAccumulatedEnergiesMicroJoules[index] >= 0L) {
+ mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ;
} else {
Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket "
- + ENERGY_BUCKET_NAMES[bucket] + " whose value was "
- + mAccumulatedEnergiesMicroJoules[bucket]);
+ + getBucketName(index) + " whose value was "
+ + mAccumulatedEnergiesMicroJoules[index]);
}
}
}
/**
- * Return accumulated energy (in microjoules) for the given energy bucket since last reset.
- * Returns {@link BatteryStats#ENERGY_DATA_UNAVAILABLE} if this energy data is unavailable.
+ * Return accumulated energy (in microjoules) for a standard energy bucket since last reset.
+ * Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable.
+ * @throws IllegalArgumentException if no such {@link StandardEnergyBucket}.
*/
- public long getAccumulatedBucketEnergy(@EnergyBucket int bucket) {
+ public long getAccumulatedStandardBucketEnergy(@StandardEnergyBucket int bucket) {
+ checkValidStandardBucket(bucket);
return mAccumulatedEnergiesMicroJoules[bucket];
}
/**
- * Map {@link MeasuredEnergySubsystem} and device state to a Display {@link EnergyBucket}.
+ * Return accumulated energy (in microjoules) for the a custom energy bucket since last reset.
+ * Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable.
*/
- public static @EnergyBucket int getDisplayEnergyBucket(int screenState) {
+ public long getAccumulatedCustomBucketEnergy(int customBucket) {
+ if (!isValidCustomBucket(customBucket)) {
+ return ENERGY_DATA_UNAVAILABLE;
+ }
+ return mAccumulatedEnergiesMicroJoules[customBucketToIndex(customBucket)];
+ }
+
+ /**
+ * Map {@link MeasuredEnergySubsystem} and device state to Display {@link StandardEnergyBucket}.
+ */
+ public static @StandardEnergyBucket int getDisplayEnergyBucket(int screenState) {
if (Display.isOnState(screenState)) {
return ENERGY_BUCKET_SCREEN_ON;
}
@@ -204,15 +262,20 @@
/**
* Create a MeasuredEnergyStats object from a summary parcel.
*
+ * Corresponding write performed by
+ * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean)}.
+ *
* @return a new MeasuredEnergyStats object as described.
* Returns null if the parcel indicates there is no data to populate.
*/
public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) {
+ final int arraySize = in.readInt();
// Check if any MeasuredEnergyStats exists on the parcel
- if (in.readInt() == 0) return null;
+ if (arraySize == 0) return null;
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(new boolean[NUMBER_ENERGY_BUCKETS]);
+ final int numCustomBuckets = arraySize - NUMBER_STANDARD_ENERGY_BUCKETS;
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(
+ new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], numCustomBuckets);
stats.readSummaryFromParcel(in, true);
return stats;
}
@@ -221,6 +284,12 @@
* Create a MeasuredEnergyStats using the template to determine which buckets are supported,
* and populate this new object from the given parcel.
*
+ * The parcel must be consistent with the template in terms of the number of
+ * possible (not necessarily supported) standard and custom buckets.
+ *
+ * Corresponding write performed by
+ * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean)}.
+ *
* @return a new MeasuredEnergyStats object as described.
* Returns null if the stats contain no non-0 information (such as if template is null
* or if the parcel indicates there is no data to populate).
@@ -229,12 +298,22 @@
*/
public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in,
@Nullable MeasuredEnergyStats template) {
+ final int arraySize = in.readInt();
// Check if any MeasuredEnergyStats exists on the parcel
- if (in.readInt() == 0) return null;
+ if (arraySize == 0) return null;
if (template == null) {
- // Nothing supported now. Create placeholder object just to consume the parcel data.
- final MeasuredEnergyStats mes = new MeasuredEnergyStats();
+ // Nothing supported anymore. Create placeholder object just to consume the parcel data.
+ final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize);
+ mes.readSummaryFromParcel(in, false);
+ return null;
+ }
+
+ if (arraySize != template.getNumberOfIndices()) {
+ Slog.wtf(TAG, "Size of MeasuredEnergyStats parcel (" + arraySize
+ + ") does not match template (" + template.getNumberOfIndices() + ").");
+ // Something is horribly wrong. Just consume the parcel and return null.
+ final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize);
mes.readSummaryFromParcel(in, false);
return null;
}
@@ -251,14 +330,17 @@
/** Returns true iff any of the buckets are supported and non-zero. */
private boolean containsInterestingData() {
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- if (mAccumulatedEnergiesMicroJoules[bucket] > 0) return true;
+ for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
+ if (mAccumulatedEnergiesMicroJoules[index] > 0) return true;
}
return false;
}
/**
* Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0.
+ *
+ * Corresponding read performed by {@link #createAndReadSummaryFromParcel(Parcel)}
+ * and {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}.
*/
public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats,
Parcel dest, boolean skipZero) {
@@ -266,14 +348,15 @@
dest.writeInt(0);
return;
}
- dest.writeInt(1);
+ dest.writeInt(stats.getNumberOfIndices());
stats.writeSummaryToParcel(dest, skipZero);
}
/** Reset accumulated energy. */
private void reset() {
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- setValueIfSupported(bucket, 0L);
+ final int numIndices = getNumberOfIndices();
+ for (int index = 0; index < numIndices; index++) {
+ setValueIfSupported(index, 0L);
}
}
@@ -282,33 +365,93 @@
if (stats != null) stats.reset();
}
- /** If the bucket is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */
- private void setValueIfSupported(@EnergyBucket int bucket, long value) {
- if (mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE) {
- mAccumulatedEnergiesMicroJoules[bucket] = value;
+ /** If the index is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */
+ private void setValueIfSupported(int index, long value) {
+ if (mAccumulatedEnergiesMicroJoules[index] != ENERGY_DATA_UNAVAILABLE) {
+ mAccumulatedEnergiesMicroJoules[index] = value;
}
}
- /** Check if measuring the energy of the given bucket is supported by this device. */
- public boolean isEnergyBucketSupported(@EnergyBucket int bucket) {
- return mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE;
+ /**
+ * Check if measuring the energy of the given bucket is supported by this device.
+ * @throws IllegalArgumentException if not a valid {@link StandardEnergyBucket}.
+ */
+ public boolean isStandardBucketSupported(@StandardEnergyBucket int bucket) {
+ checkValidStandardBucket(bucket);
+ return isIndexSupported(bucket);
+ }
+
+ private boolean isIndexSupported(int index) {
+ return mAccumulatedEnergiesMicroJoules[index] != ENERGY_DATA_UNAVAILABLE;
+ }
+
+ /** Check if the supported energy buckets are precisely those given. */
+ public boolean isSupportEqualTo(
+ @NonNull boolean[] queriedStandardBuckets, int numCustomBuckets) {
+
+ final int numBuckets = getNumberOfIndices();
+ // TODO(b/178504428): Detect whether custom buckets have changed qualitatively, not just
+ // quantitatively, and treat as mismatch if so.
+ if (numBuckets != NUMBER_STANDARD_ENERGY_BUCKETS + numCustomBuckets) {
+ return false;
+ }
+ for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
+ if (isStandardBucketSupported(stdBucket) != queriedStandardBuckets[stdBucket]) {
+ return false;
+ }
+ }
+ return true;
}
/** Dump debug data. */
public void dump(PrintWriter pw) {
pw.println("Accumulated energy since last reset (microjoules):");
pw.print(" ");
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- pw.print(ENERGY_BUCKET_NAMES[bucket]);
+ for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
+ pw.print(getBucketName(index));
pw.print(" : ");
- pw.print(mAccumulatedEnergiesMicroJoules[bucket]);
- if (!isEnergyBucketSupported(bucket)) {
+ pw.print(mAccumulatedEnergiesMicroJoules[index]);
+ if (!isIndexSupported(index)) {
pw.print(" (unsupported)");
}
- if (bucket != NUMBER_ENERGY_BUCKETS - 1) {
+ if (index != mAccumulatedEnergiesMicroJoules.length - 1) {
pw.print(", ");
}
}
pw.println();
}
+
+ /**
+ * If the index is a standard bucket, returns its name; otherwise returns its prefixed custom
+ * bucket number.
+ */
+ private static String getBucketName(int index) {
+ if (isValidStandardBucket(index)) {
+ return DebugUtils.valueToString(MeasuredEnergyStats.class, "ENERGY_BUCKET_", index);
+ }
+ return "CUSTOM_" + indexToCustomBucket(index);
+ }
+
+ private static int customBucketToIndex(int customBucket) {
+ return customBucket + NUMBER_STANDARD_ENERGY_BUCKETS;
+ }
+
+ private static int indexToCustomBucket(int index) {
+ return index - NUMBER_STANDARD_ENERGY_BUCKETS;
+ }
+
+ private static void checkValidStandardBucket(@StandardEnergyBucket int bucket) {
+ if (!isValidStandardBucket(bucket)) {
+ throw new IllegalArgumentException("Illegal StandardEnergyBucket " + bucket);
+ }
+ }
+
+ private static boolean isValidStandardBucket(@StandardEnergyBucket int bucket) {
+ return bucket >= 0 && bucket < NUMBER_STANDARD_ENERGY_BUCKETS;
+ }
+
+ private boolean isValidCustomBucket(int customBucket) {
+ return customBucket >= 0
+ && customBucketToIndex(customBucket) < mAccumulatedEnergiesMicroJoules.length;
+ }
}
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 30cd94c..f42f468 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -14,6 +14,7 @@
package com.android.internal.util;
+import android.annotation.IntDef;
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
@@ -23,9 +24,12 @@
import android.util.Log;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.EventLogTags;
import com.android.internal.os.BackgroundThread;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -91,6 +95,34 @@
*/
public static final int ACTION_START_RECENTS_ANIMATION = 8;
+ private static final int[] ACTIONS_ALL = {
+ ACTION_EXPAND_PANEL,
+ ACTION_TOGGLE_RECENTS,
+ ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+ ACTION_CHECK_CREDENTIAL,
+ ACTION_CHECK_CREDENTIAL_UNLOCKED,
+ ACTION_TURN_ON_SCREEN,
+ ACTION_ROTATE_SCREEN,
+ ACTION_FACE_WAKE_AND_UNLOCK,
+ ACTION_START_RECENTS_ANIMATION
+ };
+
+ /** @hide */
+ @IntDef({
+ ACTION_EXPAND_PANEL,
+ ACTION_TOGGLE_RECENTS,
+ ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+ ACTION_CHECK_CREDENTIAL,
+ ACTION_CHECK_CREDENTIAL_UNLOCKED,
+ ACTION_TURN_ON_SCREEN,
+ ACTION_ROTATE_SCREEN,
+ ACTION_FACE_WAKE_AND_UNLOCK,
+ ACTION_START_RECENTS_ANIMATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Action {
+ }
+
private static final int[] STATSD_ACTION = new int[]{
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
@@ -105,9 +137,14 @@
private static LatencyTracker sLatencyTracker;
+ private final Object mLock = new Object();
private final SparseLongArray mStartRtc = new SparseLongArray();
- private volatile int mSamplingInterval;
- private volatile boolean mEnabled;
+ @GuardedBy("mLock")
+ private final int[] mTraceThresholdPerAction = new int[ACTIONS_ALL.length];
+ @GuardedBy("mLock")
+ private int mSamplingInterval;
+ @GuardedBy("mLock")
+ private boolean mEnabled;
public static LatencyTracker getInstance(Context context) {
if (sLatencyTracker == null) {
@@ -132,20 +169,26 @@
}
private void updateProperties(DeviceConfig.Properties properties) {
- mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
- DEFAULT_SAMPLING_INTERVAL);
- mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+ synchronized (mLock) {
+ mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+ DEFAULT_SAMPLING_INTERVAL);
+ mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+ for (int action : ACTIONS_ALL) {
+ mTraceThresholdPerAction[action] =
+ properties.getInt(getTraceTriggerNameForAction(action), -1);
+ }
+ }
}
/**
* A helper method to translate action type to name.
*
- * @param action the action type defined in AtomsProto.java
+ * @param atomsProtoAction the action type defined in AtomsProto.java
* @return the name of the action
*/
- public static String getNameOfAction(int action) {
+ public static String getNameOfAction(int atomsProtoAction) {
// Defined in AtomsProto.java
- switch (action) {
+ switch (atomsProtoAction) {
case 0:
return "UNKNOWN";
case 1:
@@ -171,16 +214,22 @@
}
}
- private static String getTraceNameOfAction(int action) {
+ private static String getTraceNameOfAction(@Action int action) {
return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
}
+ private static String getTraceTriggerNameForAction(@Action int action) {
+ return "latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
+ }
+
public static boolean isEnabled(Context ctx) {
return getInstance(ctx).isEnabled();
}
public boolean isEnabled() {
- return mEnabled;
+ synchronized (mLock) {
+ return mEnabled;
+ }
}
/**
@@ -188,7 +237,7 @@
*
* @param action The action to start. One of the ACTION_* values.
*/
- public void onActionStart(int action) {
+ public void onActionStart(@Action int action) {
if (!isEnabled()) {
return;
}
@@ -201,7 +250,7 @@
*
* @param action The action to end. One of the ACTION_* values.
*/
- public void onActionEnd(int action) {
+ public void onActionEnd(@Action int action) {
if (!isEnabled()) {
return;
}
@@ -221,19 +270,30 @@
* @param action The action to end. One of the ACTION_* values.
* @param duration The duration of the action in ms.
*/
- public void logAction(int action, int duration) {
- boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ public void logAction(@Action int action, int duration) {
+ boolean shouldSample;
+ int traceThreshold;
+ synchronized (mLock) {
+ shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ traceThreshold = mTraceThresholdPerAction[action];
+ }
+
+ if (traceThreshold > 0 && duration >= traceThreshold) {
+ PerfettoTrigger.trigger(getTraceTriggerNameForAction(action));
+ }
+
logActionDeprecated(action, duration, shouldSample);
}
/**
* Logs an action that has started and ended. This needs to be called from the main thread.
*
- * @param action The action to end. One of the ACTION_* values.
- * @param duration The duration of the action in ms.
+ * @param action The action to end. One of the ACTION_* values.
+ * @param duration The duration of the action in ms.
* @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
*/
- public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) {
+ public static void logActionDeprecated(
+ @Action int action, int duration, boolean writeToStatsLog) {
Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java
new file mode 100644
index 0000000..9c87c69
--- /dev/null
+++ b/core/java/com/android/internal/util/PerfettoTrigger.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * A trigger implementation with perfetto backend.
+ * @hide
+ */
+public class PerfettoTrigger {
+ private static final String TAG = "PerfettoTrigger";
+ private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
+
+ /**
+ * @param triggerName The name of the trigger. Must match the value defined in the AOT
+ * Perfetto config.
+ */
+ public static void trigger(String triggerName) {
+ try {
+ ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
+ Log.v(TAG, "Triggering " + String.join(" ", pb.command()));
+ Process process = pb.start();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to trigger " + triggerName, e);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 892c5a5..50bbfc5 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -91,11 +91,12 @@
/** Remove the IME surface. Requires passing the currently focused window. */
oneway void removeImeSurfaceFromWindow(in IBinder windowToken,
in IVoidResultCallback resultCallback);
- void startProtoDump(in byte[] protoDump, int source, String where);
+ oneway void startProtoDump(in byte[] protoDump, int source, String where,
+ in IVoidResultCallback resultCallback);
oneway void isImeTraceEnabled(in IBooleanResultCallback resultCallback);
// Starts an ime trace.
- void startImeTrace();
+ oneway void startImeTrace(in IVoidResultCallback resultCallback);
// Stops an ime trace.
- void stopImeTrace();
+ oneway void stopImeTrace(in IVoidResultCallback resultCallback);
}
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index ae1a815..4b9a160 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -59,6 +59,11 @@
public static final int TYPE_RECYCLING = 2;
/**
+ * The ViewGroup scrolls, but has no child views in
+ */
+ private static final int TYPE_OPAQUE = 3;
+
+ /**
* Performs tests on the given View and determines:
* 1. If scrolling is possible
* 2. What mechanisms are used for scrolling.
@@ -95,8 +100,15 @@
}
return TYPE_RECYCLING;
}
+ // At least one child view is required.
+ if (((ViewGroup) view).getChildCount() < 1) {
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "scrollable with no children");
+ }
+ return TYPE_OPAQUE;
+ }
if (DEBUG_VERBOSE) {
- Log.v(TAG, "hint: less than two child views");
+ Log.v(TAG, "hint: single child view");
}
//Because recycling containers don't use scrollY, a non-zero value means Scroll view.
if (view.getScrollY() != 0) {
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index bad21d2..73c5460 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -148,6 +148,7 @@
private int mRegularColor;
private int mErrorColor;
private int mSuccessColor;
+ private int mDotColor;
private final Interpolator mFastOutSlowInInterpolator;
private final Interpolator mLinearOutSlowInInterpolator;
@@ -318,6 +319,7 @@
mRegularColor = a.getColor(R.styleable.LockPatternView_regularColor, 0);
mErrorColor = a.getColor(R.styleable.LockPatternView_errorColor, 0);
mSuccessColor = a.getColor(R.styleable.LockPatternView_successColor, 0);
+ mDotColor = a.getColor(R.styleable.LockPatternView_dotColor, mRegularColor);
int pathColor = a.getColor(R.styleable.LockPatternView_pathColor, mRegularColor);
mPathPaint.setColor(pathColor);
@@ -522,7 +524,7 @@
getCenterYForRow(cellState.row) + startTranslationY);
cellState.hwCenterX = CanvasProperty.createFloat(getCenterXForColumn(cellState.col));
cellState.hwRadius = CanvasProperty.createFloat(mDotSize/2 * startScale);
- mPaint.setColor(getCurrentColor(false));
+ mPaint.setColor(getDotColor());
mPaint.setAlpha((int) (startAlpha * 255));
cellState.hwPaint = CanvasProperty.createPaint(new Paint(mPaint));
@@ -1163,29 +1165,6 @@
final Path currentPath = mCurrentPath;
currentPath.rewind();
- // draw the circles
- for (int i = 0; i < 3; i++) {
- float centerY = getCenterYForRow(i);
- for (int j = 0; j < 3; j++) {
- CellState cellState = mCellStates[i][j];
- float centerX = getCenterXForColumn(j);
- float translationY = cellState.translationY;
-
- if (mUseLockPatternDrawable) {
- drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
- } else {
- if (isHardwareAccelerated() && cellState.hwAnimating) {
- RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
- recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
- cellState.hwRadius, cellState.hwPaint);
- } else {
- drawCircle(canvas, (int) centerX, (int) centerY + translationY,
- cellState.radius, drawLookup[i][j], cellState.alpha);
- }
- }
- }
- }
-
// TODO: the path should be created and cached every time we hit-detect a cell
// only the last segment of the path should be computed here
// draw the path of the pattern (unless we are in stealth mode)
@@ -1256,6 +1235,29 @@
canvas.drawPath(currentPath, mPathPaint);
}
}
+
+ // draw the circles
+ for (int i = 0; i < 3; i++) {
+ float centerY = getCenterYForRow(i);
+ for (int j = 0; j < 3; j++) {
+ CellState cellState = mCellStates[i][j];
+ float centerX = getCenterXForColumn(j);
+ float translationY = cellState.translationY;
+
+ if (mUseLockPatternDrawable) {
+ drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
+ } else {
+ if (isHardwareAccelerated() && cellState.hwAnimating) {
+ RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
+ cellState.hwRadius, cellState.hwPaint);
+ } else {
+ drawCircle(canvas, (int) centerX, (int) centerY + translationY,
+ cellState.radius, drawLookup[i][j], cellState.alpha);
+ }
+ }
+ }
+ }
}
private float calculateLastSegmentAlpha(float x, float y, float lastX, float lastY) {
@@ -1266,6 +1268,17 @@
return Math.min(1f, Math.max(0f, (frac - 0.3f) * 4f));
}
+ private int getDotColor() {
+ if (mInStealthMode) {
+ // Always use the default color in this case
+ return mDotColor;
+ } else if (mPatternDisplayMode == DisplayMode.Wrong) {
+ // the pattern is wrong
+ return mErrorColor;
+ }
+ return mDotColor;
+ }
+
private int getCurrentColor(boolean partOfPattern) {
if (!partOfPattern || mInStealthMode || mPatternInProgress) {
// unselected circle
@@ -1286,7 +1299,7 @@
*/
private void drawCircle(Canvas canvas, float centerX, float centerY, float radius,
boolean partOfPattern, float alpha) {
- mPaint.setColor(getCurrentColor(partOfPattern));
+ mPaint.setColor(getDotColor());
mPaint.setAlpha((int) (alpha * 255));
canvas.drawCircle(centerX, centerY, radius, mPaint);
}
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index ae566c3..3fc3933 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -5,3 +5,16 @@
per-file *LockScreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *LockSettings* = file:/services/core/java/com/android/server/locksettings/OWNERS
+
+# Notification related
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ImageFloatingTextView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ObservableTextView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file RemeasuringLinearLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ViewClippingUtil.java = file:/services/core/java/com/android/server/notification/OWNERS
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 66753e4..f1998a5 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -47,6 +47,7 @@
static struct assetsprovider_offsets_t {
jclass classObject;
jmethodID loadAssetFd;
+ jmethodID toString;
} gAssetsProviderOffsets;
static struct {
@@ -72,9 +73,22 @@
class LoaderAssetsProvider : public AssetsProvider {
public:
static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
- return (!assets_provider) ? nullptr
+ return (!assets_provider) ? EmptyAssetsProvider::Create()
: std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider(
- env->NewGlobalRef(assets_provider)));
+ env, assets_provider));
+ }
+
+ bool ForEachFile(const std::string& /* root_path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ return true;
+ }
+
+ const std::string& GetDebugName() const override {
+ return debug_name_;
+ }
+
+ bool IsUpToDate() const override {
+ return true;
}
~LoaderAssetsProvider() override {
@@ -142,20 +156,26 @@
*file_exists = true;
}
- return ApkAssets::CreateAssetFromFd(base::unique_fd(fd),
- nullptr /* path */,
- static_cast<off64_t>(mOffset),
- static_cast<off64_t>(mLength));
+ return AssetsProvider::CreateAssetFromFd(base::unique_fd(fd),
+ nullptr /* path */,
+ static_cast<off64_t>(mOffset),
+ static_cast<off64_t>(mLength));
}
private:
DISALLOW_COPY_AND_ASSIGN(LoaderAssetsProvider);
- explicit LoaderAssetsProvider(jobject assets_provider)
- : assets_provider_(assets_provider) { }
+ explicit LoaderAssetsProvider(JNIEnv* env, jobject assets_provider) {
+ assets_provider_ = env->NewGlobalRef(assets_provider);
+ auto string_result = static_cast<jstring>(env->CallObjectMethod(
+ assets_provider_, gAssetsProviderOffsets.toString));
+ ScopedUtfChars str(env, string_result);
+ debug_name_ = std::string(str.c_str(), str.size());
+ }
// The global reference to the AssetsProvider
jobject assets_provider_;
+ std::string debug_name_;
};
static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -170,18 +190,26 @@
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
- case FORMAT_APK:
- apk_assets = ApkAssets::Load(path.c_str(), property_flags, std::move(loader_assets));
+ case FORMAT_APK: {
+ auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
+ ZipAssetsProvider::Create(path.c_str()));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
case FORMAT_IDMAP:
apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
break;
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTable(path.c_str(), property_flags, std::move(loader_assets));
+ apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()),
+ std::move(loader_assets),
+ property_flags);
break;
- case FORMAT_DIRECTORY:
- apk_assets = ApkAssets::LoadFromDir(path.c_str(), property_flags, std::move(loader_assets));
+ case FORMAT_DIRECTORY: {
+ auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
+ DirectoryAssetsProvider::Create(path.c_str()));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
@@ -221,13 +249,17 @@
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
- case FORMAT_APK:
- apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets));
+ case FORMAT_APK: {
+ auto assets = MultiAssetsProvider::Create(
+ std::move(loader_assets),
+ ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str()));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets));
+ apk_assets = ApkAssets::LoadTable(
+ AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */),
+ std::move(loader_assets), property_flags);
break;
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
@@ -282,17 +314,20 @@
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
- case FORMAT_APK:
- apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets),
- static_cast<off64_t>(offset),
- static_cast<off64_t>(length));
+ case FORMAT_APK: {
+ auto assets = MultiAssetsProvider::Create(
+ std::move(loader_assets),
+ ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str(),
+ static_cast<off64_t>(offset), static_cast<off64_t>(length)));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets),
- static_cast<off64_t>(offset),
- static_cast<off64_t>(length));
+ apk_assets = ApkAssets::LoadTable(
+ AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */,
+ static_cast<off64_t>(offset),
+ static_cast<off64_t>(length)),
+ std::move(loader_assets), property_flags);
break;
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
@@ -310,8 +345,7 @@
}
static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
- auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
- auto apk_assets = ApkAssets::LoadEmpty(flags, std::move(loader_assets));
+ auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
return reinterpret_cast<jlong>(apk_assets.release());
}
@@ -458,6 +492,8 @@
gAssetsProviderOffsets.loadAssetFd = GetMethodIDOrDie(
env, gAssetsProviderOffsets.classObject, "loadAssetFd",
"(Ljava/lang/String;I)Landroid/content/res/AssetFileDescriptor;");
+ gAssetsProviderOffsets.toString = GetMethodIDOrDie(
+ env, gAssetsProviderOffsets.classObject, "toString", "()Ljava/lang/String;");
jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 5f2dcdf..5630a1e 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -41,6 +41,10 @@
#define ENCODING_OPUS 20
#define ENCODING_PCM_24BIT_PACKED 21
#define ENCODING_PCM_32BIT 22
+#define ENCODING_MPEGH_BL_L3 23
+#define ENCODING_MPEGH_BL_L4 24
+#define ENCODING_MPEGH_LC_L3 25
+#define ENCODING_MPEGH_LC_L4 26
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -98,6 +102,14 @@
return AUDIO_FORMAT_PCM_24_BIT_PACKED;
case ENCODING_PCM_32BIT:
return AUDIO_FORMAT_PCM_32_BIT;
+ case ENCODING_MPEGH_BL_L3:
+ return AUDIO_FORMAT_MPEGH_BL_L3;
+ case ENCODING_MPEGH_BL_L4:
+ return AUDIO_FORMAT_MPEGH_BL_L4;
+ case ENCODING_MPEGH_LC_L3:
+ return AUDIO_FORMAT_MPEGH_LC_L3;
+ case ENCODING_MPEGH_LC_L4:
+ return AUDIO_FORMAT_MPEGH_LC_L4;
default:
return AUDIO_FORMAT_INVALID;
}
@@ -159,6 +171,14 @@
return ENCODING_DOLBY_MAT;
case AUDIO_FORMAT_OPUS:
return ENCODING_OPUS;
+ case AUDIO_FORMAT_MPEGH_BL_L3:
+ return ENCODING_MPEGH_BL_L3;
+ case AUDIO_FORMAT_MPEGH_BL_L4:
+ return ENCODING_MPEGH_BL_L4;
+ case AUDIO_FORMAT_MPEGH_LC_L3:
+ return ENCODING_MPEGH_LC_L3;
+ case AUDIO_FORMAT_MPEGH_LC_L4:
+ return ENCODING_MPEGH_LC_L4;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 787d348..3acbd1e 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -36,6 +36,7 @@
#include <utils/List.h>
#include <utils/KeyedVector.h>
#include <binder/Parcel.h>
+#include <binder/ParcelRef.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/threads.h>
@@ -515,8 +516,9 @@
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
- Parcel* parcel = new Parcel();
- return reinterpret_cast<jlong>(parcel);
+ sp<ParcelRef> parcelRef = ParcelRef::create();
+ parcelRef->incStrong(reinterpret_cast<const void*>(android_os_Parcel_create));
+ return reinterpret_cast<jlong>(static_cast<Parcel *>(parcelRef.get()));
}
static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -529,8 +531,8 @@
static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
{
- Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
- delete parcel;
+ ParcelRef* derivative = static_cast<ParcelRef*>(reinterpret_cast<Parcel*>(nativePtr));
+ derivative->decStrong(reinterpret_cast<const void*>(android_os_Parcel_create));
}
static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 675648a..f7b3f30 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -35,6 +35,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
+#include <binder/ParcelRef.h>
#include <binder/ProcessState.h>
#include <binder/Stability.h>
#include <binderthreadstate/CallerUtils.h>
@@ -1367,7 +1368,8 @@
}
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
- jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
+ jint code, jobject dataObj, jobject replyObj, jboolean replyObjOwnsNativeParcel,
+ jint flags) // throws RemoteException
{
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -1409,6 +1411,21 @@
status_t err = target->transact(code, *data, reply, flags);
//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
+ if (reply) {
+ if (replyObjOwnsNativeParcel) {
+ // as per Parcel java class constructor, here, "reply" MUST be a "ParcelRef"
+ // only for Parcel that contained Binder objects
+ if (reply->objectsCount() > 0) {
+ IPCThreadState::self()->createTransactionReference(static_cast<ParcelRef*>(reply));
+ }
+ } else {
+ // as per Parcel.java, if Parcel java object NOT owning native Parcel object, it will
+ // NOT destroy the native Parcel object upon GC(finalize()), so, there will be no race
+ // condtion in this case. Please refer to the java class methods: Parcel.finalize(),
+ // Parcel.destroy().
+ }
+ }
+
if (kEnableBinderSample) {
if (time_binder_calls) {
conditionally_log_binder_call(start_millis, target, code);
@@ -1535,7 +1552,7 @@
{"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder},
{"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive},
{"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
- {"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
+ {"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;ZI)Z", (void*)android_os_BinderProxy_transact},
{"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
{"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 4eaa016..9cc7243 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -70,7 +70,8 @@
deviceInfo.isExternal(), deviceInfo.getSources(),
deviceInfo.getKeyboardType(), kcmObj.get(),
deviceInfo.hasVibrator(), hasMic,
- deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor()));
+ deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor(),
+ deviceInfo.hasBattery()));
const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
for (const InputDeviceInfo::MotionRange& range: ranges) {
@@ -90,9 +91,10 @@
gInputDeviceClassInfo.clazz = FindClassOrDie(env, "android/view/InputDevice");
gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz);
- gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
- "(IIILjava/lang/String;IILjava/lang/"
- "String;ZIILandroid/view/KeyCharacterMap;ZZZZ)V");
+ gInputDeviceClassInfo.ctor =
+ GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
+ "(IIILjava/lang/String;IILjava/lang/"
+ "String;ZIILandroid/view/KeyCharacterMap;ZZZZZ)V");
gInputDeviceClassInfo.addMotionRange = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz,
"addMotionRange", "(IIFFFFF)V");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4ef63ae..05fcaec 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -27,6 +27,7 @@
#include <android-base/chrono_utils.h>
#include <android/graphics/region.h>
#include <android/gui/BnScreenCaptureListener.h>
+#include <android/os/IInputConstants.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <android_runtime/android_view_Surface.h>
@@ -110,10 +111,12 @@
static struct {
jfieldID pixelFormat;
jfieldID sourceCrop;
- jfieldID frameScale;
+ jfieldID frameScaleX;
+ jfieldID frameScaleY;
jfieldID captureSecureLayers;
jfieldID allowProtected;
jfieldID uid;
+ jfieldID grayscale;
} gCaptureArgsClassInfo;
static struct {
@@ -380,13 +383,17 @@
captureArgs.sourceCrop =
rectFromObj(env,
env->GetObjectField(captureArgsObject, gCaptureArgsClassInfo.sourceCrop));
- captureArgs.frameScale =
- env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScale);
+ captureArgs.frameScaleX =
+ env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleX);
+ captureArgs.frameScaleY =
+ env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleY);
captureArgs.captureSecureLayers =
env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.captureSecureLayers);
captureArgs.allowProtected =
env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.allowProtected);
captureArgs.uid = env->GetLongField(captureArgsObject, gCaptureArgsClassInfo.uid);
+ captureArgs.grayscale =
+ env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.grayscale);
}
static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
@@ -1619,7 +1626,8 @@
jlong frameTimelineVsyncId) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->setFrameTimelineVsync(frameTimelineVsyncId);
+ transaction->setFrameTimelineInfo(
+ {frameTimelineVsyncId, android::os::IInputConstants::INVALID_INPUT_EVENT_ID});
}
class JankDataListenerWrapper : public JankDataListener {
@@ -2032,12 +2040,14 @@
gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I");
gCaptureArgsClassInfo.sourceCrop =
GetFieldIDOrDie(env, captureArgsClazz, "mSourceCrop", "Landroid/graphics/Rect;");
- gCaptureArgsClassInfo.frameScale = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScale", "F");
+ gCaptureArgsClassInfo.frameScaleX = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleX", "F");
+ gCaptureArgsClassInfo.frameScaleY = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleY", "F");
gCaptureArgsClassInfo.captureSecureLayers =
GetFieldIDOrDie(env, captureArgsClazz, "mCaptureSecureLayers", "Z");
gCaptureArgsClassInfo.allowProtected =
GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z");
gCaptureArgsClassInfo.uid = GetFieldIDOrDie(env, captureArgsClazz, "mUid", "J");
+ gCaptureArgsClassInfo.grayscale = GetFieldIDOrDie(env, captureArgsClazz, "mGrayscale", "Z");
jclass displayCaptureArgsClazz =
FindClassOrDie(env, "android/view/SurfaceControl$DisplayCaptureArgs");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c76a5d3..c5a9559 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -70,7 +70,6 @@
#include <bionic/malloc.h>
#include <bionic/mte.h>
#include <cutils/fs.h>
-#include <cutils/memory.h>
#include <cutils/multiuser.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
@@ -621,13 +620,6 @@
// Set the jemalloc decay time to 1.
mallopt(M_DECAY_TIME, 1);
-
- // Avoid potentially expensive memory mitigations, mostly meant for system
- // processes, in apps. These may cause app compat problems, use more memory,
- // or reduce performance. While it would be nice to have them for apps,
- // we will have to wait until they are proven out, have more efficient
- // hardware, and/or apply them only to new applications.
- process_disable_memory_mitigations();
}
static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
@@ -1785,6 +1777,14 @@
break;
}
mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level);
+
+ // Avoid heap zero initialization for applications without MTE. Zero init may
+ // cause app compat problems, use more memory, or reduce performance. While it
+ // would be nice to have them for apps, we will have to wait until they are
+ // proven out, have more efficient hardware, and/or apply them only to new
+ // applications.
+ mallopt(M_BIONIC_ZERO_INIT, 0);
+
// Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index f5aa17d..632d372 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -518,6 +518,11 @@
}
optional Search search = 48;
+ message CameraAutorotate {
+ optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional CameraAutorotate camera_autorotate = 88;
+
message SpellChecker {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -642,5 +647,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 88;
+ // Next tag = 89;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4a0a35d..5442c03 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1748,7 +1748,7 @@
@SystemApi
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows applications to toggle airplane mode.
<p>Not for use by third-party or privileged applications.
@@ -2541,7 +2541,7 @@
<!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
@hide -->
<permission android:name="android.permission.START_TASKS_FROM_RECENTS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|recents" />
<!-- @SystemApi @hide Allows an application to call APIs that allow it to do interactions
across the users on the device, using singleton services and
@@ -2606,7 +2606,7 @@
<!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature|documenter" />
+ android:protectionLevel="signature|documenter|recents" />
<!-- @deprecated Use MANAGE_ACTIVITY_TASKS instead.
@SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
@@ -2615,7 +2615,7 @@
<!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove tasks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|recents" />
<!-- @SystemApi @TestApi @hide Allows an application to embed other activities -->
<permission android:name="android.permission.ACTIVITY_EMBEDDING"
@@ -2708,13 +2708,13 @@
shown on top of all other apps.
Allows an application to use
- {@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY}
+ {@link android.view.WindowManager.LayoutsParams#setSystemApplicationOverlay(boolean)}
to create overlays that will stay visible, even if another window is requesting overlays to
be hidden through {@link android.view.Window#setHideOverlayWindows(boolean)}.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY"
- android:protectionLevel="signature|wellbeing"/>
+ android:protectionLevel="signature|recents|wellbeing"/>
<!-- @deprecated Use {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND}
@hide
@@ -3284,7 +3284,7 @@
and its icons.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.STATUS_BAR"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|recents" />
<!-- Allows an application to trigger bugreport via shell using the bugreport API.
<p>Not for use by third-party applications.
@@ -3451,7 +3451,7 @@
critical UI such as the home screen.
@hide -->
<permission android:name="android.permission.STOP_APP_SWITCHES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|recents" />
<!-- @SystemApi Allows an application to retrieve private information about
the current top activity, such as any assist context it can provide.
@@ -3836,7 +3836,7 @@
@hide
-->
<permission android:name="android.permission.SET_ORIENTATION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|recents" />
<!-- @SystemApi Allows low-level access to setting the pointer speed.
<p>Not for use by third-party applications.
@@ -4100,7 +4100,7 @@
@hide
@removed -->
<permission android:name="android.permission.READ_FRAME_BUFFER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|recents" />
<!-- Allows an application to use InputFlinger's low level features.
@hide -->
@@ -4133,11 +4133,6 @@
<permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to use background blur.
- @hide -->
- <permission android:name="android.permission.USE_BACKGROUND_BLUR"
- android:protectionLevel="signature|privileged" />
-
<!-- Allows an application to control the lights on the device.
@hide
@SystemApi
@@ -5168,6 +5163,11 @@
<permission android:name="android.permission.MANAGE_SEARCH_UI"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an application to manage the smartspace service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_SMARTSPACE"
+ android:protectionLevel="signature" />
+
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@hide <p>Not for use by third-party applications.</p> -->
@@ -5277,7 +5277,7 @@
<!-- @SystemApi Allows modifying accessibility state.
@hide -->
<permission android:name="android.permission.MANAGE_ACCESSIBILITY"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup|recents" />
<!-- @SystemApi Allows an app to grant a profile owner access to device identifiers.
<p>Not for use by third-party applications.
@@ -5293,7 +5293,8 @@
android:protectionLevel="signature" />
<!-- Allows financial apps to read filtered sms messages.
- Protection level: signature|appop -->
+ Protection level: signature|appop
+ @deprecated The API that used this permission is no longer functional. -->
<permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
android:protectionLevel="signature|appop" />
@@ -5411,11 +5412,12 @@
<permission android:name="android.permission.CONTROL_DEVICE_STATE"
android:protectionLevel="signature" />
- <!-- Must be required by an {@link android.service.attestation.ImpressionAttestationService}
- to ensure that only the system can bind to it.
- @hide This is not a third-party API (intended for OEMs and system apps).
+ <!-- Must be required by a
+ {@link android.service.screenshot.ScreenshotHasherService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
-->
- <permission android:name="android.permission.BIND_IMPRESSION_ATTESTATION_SERVICE"
+ <permission android:name="android.permission.BIND_SCREENSHOT_HASHER_SERVICE"
android:protectionLevel="signature" />
<!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.
diff --git a/core/res/OWNERS b/core/res/OWNERS
index a30111b..9d739b9 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -6,9 +6,12 @@
dupin@google.com
hackbod@android.com
hackbod@google.com
+ilyamaty@google.com
+jaggies@google.com
jsharkey@android.com
jsharkey@google.com
juliacr@google.com
+kchyn@google.com
michaelwr@google.com
nandana@google.com
narayan@google.com
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index eb45a6a..55eaaf6 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Net Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g>-oorkruis-SIM-oproepe"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>-rugsteunoproepe"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nie aangestuur nie"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> na <xliff:g id="TIME_DELAY">{2}</xliff:g> sekondes"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Sensorkennisgewingdiens"</string>
<string name="twilight_service" msgid="8964898045693187224">"Skemerdiens"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Tydsonebespeurder (geen konnektiwiteit nie)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS-tydopdateringdiens"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Jou toestel sal uitgevee word"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Die administrasieprogram kan nie gebruik word nie. Jou toestel sal nou uitgevee word.\n\nKontak jou organisasie se administrateur as jy vrae het."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Druk is gedeaktiveer deur <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Jy kan nou jou hele skerm of \'n deel daarvan vergroot"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Skakel aan in Instellings"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Maak toe"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Om voort te gaan, moet <b><xliff:g id="APP">%s</xliff:g></b> toegang tot jou toestel se mikrofoon hê."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Om voort te gaan, moet <b><xliff:g id="APP">%s</xliff:g></b> toegang tot jou toestel se kamera hê."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Skakel aan"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorprivaatheid"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 43a14dc..3161226 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi ብቻ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> የሲም መካከል የሚደረግ ጥሪ"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ምትኬ ጥሪ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>፡አልተላለፈም"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>፡<xliff:g id="DIALING_NUMBER">{1}</xliff:g> ከ<xliff:g id="TIME_DELAY">{2}</xliff:g> ሰከንዶች በኋላ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 4469117..a2d3671 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -152,7 +152,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi فقط"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> الاتصال عبر شرائح SIM"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانية"</string>
@@ -211,8 +212,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"خدمة إشعارات جهاز الاستشعار"</string>
<string name="twilight_service" msgid="8964898045693187224">"خدمة الغسق"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"أداة التعرّف على المنطقة الزمنية (ليس هناك حاجة للاتصال بالشبكة)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"خدمة تعديل وقت GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"سيتم محو بيانات جهازك."</string>
<string name="factory_reset_message" msgid="2657049595153992213">"تعذّر استخدام تطبيق المشرف. سيتم محو بيانات جهازك الآن.\n\nإذا كانت لديك أسئلة، اتصل بمشرف مؤسستك."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"تم إيقاف الطباعة بواسطة <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2341,12 +2341,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"يمكنك الآن تكبير الشاشة كلها أو جزء منها."</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"التفعيل من خلال \"الإعدادات\""</string>
<string name="dismiss_action" msgid="1728820550388704784">"إغلاق"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"للمتابعة، يحتاج <b><xliff:g id="APP">%s</xliff:g></b> إلى الوصول إلى ميكروفون الجهاز."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"للمتابعة، يحتاج تطبيق <b><xliff:g id="APP">%s</xliff:g></b> إلى الوصول إلى كاميرا الجهاز."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"تفعيل"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"الخصوصية في جهاز الاستشعار"</string>
</resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index b6eb959..0a9ef97 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"কোৱল ৱাই-ফাই"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ক্ৰছ-ছিম কলিং"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফৰৱাৰ্ড কৰা নহ\'ল"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ছেকেণ্ডৰ পাছত"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index f7e4169..e91e2b7 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Yalnız Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Çarpaz SİM Zəngi"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Yedək Zəng"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yönləndirilmədi"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> saniyə sonra"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Sensor Bildiriş Xidməti"</string>
<string name="twilight_service" msgid="8964898045693187224">"Alaqaranlıq Xidməti"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Saat Qurşağı Aşkarlayıcısı (Bağlantı yoxdur)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS Zaman Güncəlləmə Xidməti"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Cihazınız təmizlənəcəkdir"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Admin tətbiqini istifadə etmək mümkün deyil. Cihaz indi təmizlənəcək.\n\nSualınız varsa, təşkilatın admini ilə əlaqə saxlayın."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Çap <xliff:g id="OWNER_APP">%s</xliff:g> tərəfindən deaktiv edildi."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"İndi ekranı qismən və ya tam şəkildə böyüdə bilərsiniz"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ayarlarda aktiv edin"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Qapadın"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Davam etmək üçün <b><xliff:g id="APP">%s</xliff:g></b> tətbiqi cihazın mikrofonuna giriş tələb edir."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Davam etmək üçün <b><xliff:g id="APP">%s</xliff:g></b> tətbiqi cihazın kamerasına giriş tələb edir."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktiv edin"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensor Məxfiliyi"</string>
</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 76a278a..a490a6c 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -149,7 +149,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Pozivi sa više SIM kartica za operatera <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> rezervni način za pozivanje"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije prosleđeno"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> nakon <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunde/i"</string>
@@ -205,8 +205,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Usluga obaveštenja senzora"</string>
<string name="twilight_service" msgid="8964898045693187224">"Usluga Sumrak"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detektor vremenske zone (nema internet veze)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS usluga za ažuriranje vremena"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Uređaj će biti obrisan"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Ne možete da koristite ovu aplikaciju za administratore. Uređaj će sada biti obrisan.\n\nAko imate pitanja, kontaktirajte administratora organizacije."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Štampanje je onemogućila aplikacija <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2239,12 +2238,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Možete da uvećate deo ekrana ili ceo ekran"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Uključite u Podešavanjima"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Odbaci"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"<b><xliff:g id="APP">%s</xliff:g></b> zahteva pristup mikrofonu uređaja radi nastavljanja."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> zahteva pristup kameri uređaja radi nastavljanja."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Uključi"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatnost senzora"</string>
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index deb9218..80f67d0 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -150,7 +150,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Толькі Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Тэлефанія паміж SIM-картамі"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не пераадрасоўваецца"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> праз <xliff:g id="TIME_DELAY">{2}</xliff:g> с."</string>
@@ -207,8 +208,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Служба апавяшчэнняў датчыка"</string>
<string name="twilight_service" msgid="8964898045693187224">"Служба Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Дэтэктар часавога пояса (няма падключэння)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Служба абнаўлення часу GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Даныя вашай прылады будуць сцерты"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Немагчыма выкарыстоўваць праграму адміністравання. Звесткі на вашай прыладзе будуць выдалены.\n\nКалі ў вас ёсць пытанні, звярніцеся да адміністратара арганізацыі."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Друк адключаны ўладальнікам праграмы <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2273,12 +2273,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Цяпер можна павялічваць увесь экран ці яго частку."</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Уключыць у Наладах"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Адхіліць"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Каб працягнуць, дайце праграме <b><xliff:g id="APP">%s</xliff:g></b> доступ да мікрафона прылады."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Каб працягнуць, дайце праграме <b><xliff:g id="APP">%s</xliff:g></b> доступ да камеры прылады."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Уключыць"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Прыватнасць інфармацыі з датчыка"</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 9ee0d99..20342e0 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Обаждания през друга SIM карта от <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Обаждания през друга SIM карта от <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Не е пренасочено"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> след <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Услуга за известия за сензорите"</string>
<string name="twilight_service" msgid="8964898045693187224">"Услуга Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Инструмент за установяване на часовата зона (няма връзка)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Услуга на GNSS за актуализиране на часа"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Данните на устройството ви ще бъдат изтрити"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Приложението за администриране не може да се използва. Сега данните на устройството ви ще бъдат изтрити.\n\nАко имате въпроси, свържете се с администратора на организацията си."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Отпечатването е деактивиранo от <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Можете да увеличите целия екран или част от него"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Включете от настройките"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Отхвърляне"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"За да продължите, <b><xliff:g id="APP">%s</xliff:g></b> се нуждае от достъп до микрофона на устройството ви."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"За да продължите, <b><xliff:g id="APP">%s</xliff:g></b> се нуждае от достъп до камерата на устройството ви."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Включване"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Поверителност на сензорните данни"</string>
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 3ce96c7..51d09b2 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"শুধুমাত্র ওয়াই-ফাই"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ক্রস সিম কল করা"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফরওয়ার্ড করা হয়নি"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> সেকেন্ড পরে"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"সেন্সর বিজ্ঞপ্তি পরিষেবা"</string>
<string name="twilight_service" msgid="8964898045693187224">"গোধূলি পরিষেবা"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"টাইম জোন ডিটেক্টর (কানেকশন নেই)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS সময় আপডেট পরিষেবা"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"আপনার ডিভাইসটি মুছে ফেলা হবে"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"অ্যাডমিন অ্যাপটি ব্যবহার করা যাবে না। আপনার ডিভাইসে থাকা সবকিছু এখন মুছে ফেলা হবে।\n\nকোনও প্রশ্ন থাকলে আপনার প্রতিষ্ঠানের অ্যাডমিনের সাথে যোগাযোগ করুন।"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> প্রিন্টিং বন্ধ রেখেছে।"</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"এখন আপনি কিছু বা সবকটি স্ক্রিন বড় করে দেখতে পারেন"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"সেটিংস থেকে চালু করুন"</string>
<string name="dismiss_action" msgid="1728820550388704784">"বাতিল করুন"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"চালিয়ে যেতে, <b><xliff:g id="APP">%s</xliff:g></b> আপনার ডিভাইসের মাইক্রোফোন অ্যাক্সেস করতে চায়।"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"চালিয়ে যেতে, <b><xliff:g id="APP">%s</xliff:g></b> আপনার ডিভাইসের ক্যামেরা অ্যাক্সেস করতে চায়।"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"চালু করুন"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"সেন্সর গোপনীয়তা"</string>
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 6c54eb1..989e2bb 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -149,7 +149,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – pozivanje na različitim SIM-ovima"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> – pomoćno pozivanje"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije proslijeđen"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> za <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundi"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 6d81104..e9c55d7 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Només Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Trucades entre targetes SIM de l\'operador <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Trucades alternatives (<xliff:g id="SPN">%s</xliff:g>)"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no s\'ha desviat"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> després de <xliff:g id="TIME_DELAY">{2}</xliff:g> segons"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Servei de notificacions de sensor"</string>
<string name="twilight_service" msgid="8964898045693187224">"Servei Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detector de zona horària (sense connectivitat)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Servei GNSS d\'actualització horària"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"El contingut del dispositiu s\'esborrarà"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"No es pot utilitzar l\'aplicació d\'administració. S\'esborraran les dades del dispositiu.\n\nSi tens cap dubte, contacta amb l\'administrador de la teva organització."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ha desactivat la impressió."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Ara pots ampliar la pantalla completa o una part"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activa a Configuració"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Ignora"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Per continuar, <b><xliff:g id="APP">%s</xliff:g></b> necessita accedir al micròfon del dispositiu."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Per continuar, <b><xliff:g id="APP">%s</xliff:g></b> necessita accedir a la càmera del dispositiu."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activa"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privadesa dels sensors"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 00b607c..071bbfd 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -150,7 +150,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Pouze Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – volání napříč SIM kartami"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Záložní volání"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nepřesměrováno"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sek."</string>
@@ -207,8 +207,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Služba oznámení ze senzoru"</string>
<string name="twilight_service" msgid="8964898045693187224">"Služba detekce soumraku"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detektor časového pásma (bez připojení)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS – služba pro aktualizaci času"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Zařízení bude vymazáno"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Aplikaci pro správu nelze použít. Zařízení teď bude vymazáno.\n\nV případě dotazů vám pomůže administrátor organizace."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Aplikace <xliff:g id="OWNER_APP">%s</xliff:g> tisk zakazuje."</string>
@@ -2273,12 +2272,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Nyní můžete zvětšit celou obrazovku nebo její část"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Zapnout v Nastavení"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Zavřít"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Než budete pokračovat, udělte aplikaci <b><xliff:g id="APP">%s</xliff:g></b> přístup k mikrofonu na zařízení."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Než budete pokračovat, udělte aplikaci <b><xliff:g id="APP">%s</xliff:g></b> přístup k fotoaparátu na zařízení."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Zapnout"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ochrana soukromí – senzor"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 6e9a7e7..b760f5b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Kun Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Opkald på tværs af SIM-kort"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Alternativ løsning til opkald leveret af <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderestillet"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Tjenesten Sensor Notification"</string>
<string name="twilight_service" msgid="8964898045693187224">"Tjenesten Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Tidszoneregistrering (ingen forbindelse)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Tjeneste til opdatering af GNSS-tid"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Enheden slettes"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Administrationsappen kan ikke bruges. Enheden vil nu blive ryddet. \n\nKontakt din organisations administrator, hvis du har spørgsmål."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Udskrivning er deaktiveret af <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2207,12 +2206,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Du kan nu forstørre dele af eller hele skærmen"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Aktivér i Indstillinger"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Luk"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"<b><xliff:g id="APP">%s</xliff:g></b> skal have adgang til din enheds mikrofon, før den kan fortsætte."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> skal have adgang til din enheds kamera, før den kan fortsætte."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktivér"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Beskyttelse af sensoroplysninger"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ef7e628..f8066e2 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Nur WLAN"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM-übergreifende Anrufe"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nicht weitergeleitet"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> nach <xliff:g id="TIME_DELAY">{2}</xliff:g> Sekunden."</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Sensor Notification Service"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight Service"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Zeitzonen-Erkennung (keine Verbindung)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS-Zeitaktualisierungsdienst"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Die Daten auf deinem Gerät werden gelöscht."</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Die Admin-App kann nicht verwendet werden. Die Daten auf deinem Gerät werden nun gelöscht.\n\nBitte wende dich bei Fragen an den Administrator deiner Organisation."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Drucken wurde von <xliff:g id="OWNER_APP">%s</xliff:g> deaktiviert."</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Du kannst das Display teilweise oder ganz vergrößern"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"In den Einstellungen aktivieren"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Schließen"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Zum Fortfahren benötigt, <b><xliff:g id="APP">%s</xliff:g></b> Zugriff auf das Mikrofon deines Geräts."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Zum Fortfahren benötigt <b><xliff:g id="APP">%s</xliff:g></b> Zugriff auf die Kamera deines Geräts."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktivieren"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Datenschutz für Sensoren"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 614fc95..4f3c8a9 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Μόνο Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Κλήση με πολλές SIM <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Δημιουργία αντιγράφων ασφαλείας κλήσεων"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Δεν προωθήθηκε"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> μετά από <xliff:g id="TIME_DELAY">{2}</xliff:g> δευτερόλεπτα"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Υπηρεσία ειδοποίησης αισθητήρα"</string>
<string name="twilight_service" msgid="8964898045693187224">"Υπηρεσία Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Εντοπισμός ζώνης ώρας (χωρίς συνδεσιμότητα)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Υπηρεσία ενημέρωσης ώρας GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Η συσκευή σας θα διαγραφεί"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Δεν είναι δυνατή η χρήση της εφαρμογής διαχειριστή. Η συσκευή σας θα διαγραφεί.\n\nΕάν έχετε ερωτήσεις, επικοινωνήστε με τον διαχειριστή του οργανισμού σας."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Η εκτύπωση απενεργοποιήθηκε από τον χρήστη <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Μεγεθύνετε μέρος ή ολόκληρη την οθόνη σας"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ενεργοποίηση στις Ρυθμίσεις"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Παράβλεψη"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Για να συνεχίσετε, η εφαρμογή <b><xliff:g id="APP">%s</xliff:g></b> χρειάζεται πρόσβαση στο μικρόφωνο της συσκευής σας."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Για να συνεχίσετε, η εφαρμογή <b><xliff:g id="APP">%s</xliff:g></b> χρειάζεται πρόσβαση στην κάμερα της συσκευής σας."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Ενεργοποίηση"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Απόρρητο αισθητήρα"</string>
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 6d760d0..9f3bc7d 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM calling"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Backup calling"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index c529338..766e372 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM calling"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Backup calling"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 1d4a614..16da211 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM calling"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Backup calling"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 0b7e11a..150830e 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM calling"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Backup calling"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 0fbfcc3..e952cac 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi only"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM Calling"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Backup Calling"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 75973ae..c5ff193 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Llamadas entre tarjetas SIM de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Llamada de copia de seguridad de <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha remitido"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> después de <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index e84fd00..108c3db 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Llamadas entre tarjetas SIM de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Llamadas de reserva de <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: No desviada"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> transcurridos <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Servicio de notificación de sensor"</string>
<string name="twilight_service" msgid="8964898045693187224">"Servicio de Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detector de zona horaria (sin conexión)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Servicio de actualización de tiempo GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Tu dispositivo se borrará"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"No se puede utilizar la aplicación de administración. Se borrarán todos los datos del dispositivo.\n\nSi tienes alguna pregunta, ponte en contacto con el administrador de tu organización."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ha inhabilitado la impresión."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Ahora puedes ampliar toda la pantalla o una parte"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activar en Ajustes"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Cerrar"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> necesita tener acceso al micrófono del dispositivo."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> necesita tener acceso a la cámara del dispositivo."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activar"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidad del sensor"</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index f7d053d..70862d4 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Ainult WiFi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – helistamine mitme SIM-kaardi kaudu"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> – helistamise varuviis"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: pole suunatud"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundi pärast"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Anduri märguande teenus"</string>
<string name="twilight_service" msgid="8964898045693187224">"Teenus Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Ajavööndi tuvastaja (ühenduvus puudub)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS-i aja värskendamise teenus"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Seade kustutatakse"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Administraatori rakendust ei saa kasutada. Teie seade tühjendatakse nüüd.\n\nKui teil on küsimusi, võtke ühendust organisatsiooni administraatoriga."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Rakendus <xliff:g id="OWNER_APP">%s</xliff:g> on printimise keelanud."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Nüüd saab suurendada kogu ekraanikuva või osa sellest"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Lülitage sisse menüüs Seaded"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Loobu"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Jätkamiseks vajab rakendus <b><xliff:g id="APP">%s</xliff:g></b> juurdepääsu teie seadme mikrofonile."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Jätkamiseks vajab rakendus <b><xliff:g id="APP">%s</xliff:g></b> juurdepääsu teie seadme kaamerale."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Lülita sisse"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Anduri privaatsus"</string>
</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index b22a777..bc691a5 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wifi-sarea soilik"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> operadorearen beste SIM txartel batetik deitzeko aukera"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ez da desbideratu"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> zenbakira <xliff:g id="TIME_DELAY">{2}</xliff:g> segundotan"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Sentsorearen jakinarazpen-zerbitzua"</string>
<string name="twilight_service" msgid="8964898045693187224">"Ilunabarreko zerbitzua"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Ordu-zonaren hautemailea (ez zaude konektatuta sarera)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS ordua eguneratzeko zerbitzua"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Gailuko datuak ezabatu egingo dira"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Ezin da erabili administratzeko aplikazioa. Ezabatu egingo da gailuko eduki guztia.\n\nZalantzarik baduzu, jarri erakundeko administratzailearekin harremanetan."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> aplikazioak desgaitu egin du inprimatzeko aukera."</string>
@@ -1589,7 +1589,7 @@
<string name="display_manager_built_in_display_name" msgid="1015775198829722440">"Pantaila integratua"</string>
<string name="display_manager_hdmi_display_name" msgid="1022758026251534975">"HDMI pantaila"</string>
<string name="display_manager_overlay_display_name" msgid="5306088205181005861">"<xliff:g id="ID">%1$d</xliff:g>. gainjartzea"</string>
- <string name="display_manager_overlay_display_title" msgid="1480158037150469170">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g> x <xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
+ <string name="display_manager_overlay_display_title" msgid="1480158037150469170">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g> × <xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
<string name="display_manager_overlay_display_secure_suffix" msgid="2810034719482834679">", segurua"</string>
<string name="kg_forgot_pattern_button_text" msgid="406145459223122537">"Eredua ahaztu zaizu"</string>
<string name="kg_wrong_pattern" msgid="1342812634464179931">"Eredu okerra"</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Orain, pantaila osoa edo haren zati bat handi dezakezu"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Aktibatu ezarpenetan"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Baztertu"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Aurrera egiteko, gailuaren mikrofonoa atzitzeko baimena behar du <b><xliff:g id="APP">%s</xliff:g></b> aplikazioak."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Aurrera egiteko, gailuaren kamera atzitzeko baimena behar du <b><xliff:g id="APP">%s</xliff:g></b> aplikazioak."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktibatu"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sentsoreen pribatutasuna"</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 98d715b..6a0e454 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"فقط Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"تماس بین سیمکارت <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> تماس پشتیبان"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: هدایت نشده"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> پس از <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانیه"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 159e1b4..8e5d9b4 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Vain Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"SIM-korttien väliset puhelut: <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Puheluiden varavaihtoehto"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ei siirretty"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunnin päästä"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Anturin ilmoituspalvelu"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight-palvelu"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Aikavyöhykkeen tunnistin (ei yhteyttä)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS-ajanpäivityspalvelu"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Laitteen tiedot poistetaan"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Hallintasovellusta ei voi käyttää. Laitteen tiedot pyyhitään.\n\nPyydä ohjeita järjestelmänvalvojaltasi."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> on poistanut tulostuksen käytöstä."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Voit nyt suurentaa näytön osittain tai kokonaan"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Laita päälle asetuksista"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Hylkää"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Jotta voit jatkaa, <b><xliff:g id="APP">%s</xliff:g></b> tarvitsee pääsyn laitteesi mikrofoniin."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Jotta voit jatkaa, <b><xliff:g id="APP">%s</xliff:g></b> tarvitsee pääsyn laitteesi kameraan."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Laita päälle"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Anturin tietosuoja"</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index e34bf8b..849f9594 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi seulement"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Appels multiSIM avec <xliff:g id="SPN">%s</xliff:g>"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b6eade2..60138c4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi uniquement"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Appels par cartes SIM croisées <xliff:g id="SPN">%s</xliff:g>"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Service de notification du capteur"</string>
<string name="twilight_service" msgid="8964898045693187224">"Service Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Outil de détection du fuseau horaire (aucune connectivité)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Service de mise à jour de l\'heure GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Les données de votre appareil vont être effacées"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Impossible d\'utiliser l\'application d\'administration. Les données de votre appareil vont maintenant être effacées.\n\nSi vous avez des questions, contactez l\'administrateur de votre organisation."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Impression désactivée par <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Vous pouvez agrandir tout ou partie de l\'écran"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activer dans les paramètres"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Fermer"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Pour continuer, <b><xliff:g id="APP">%s</xliff:g></b> a besoin d\'accéder au micro de votre appareil."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Pour continuer, <b><xliff:g id="APP">%s</xliff:g></b> a besoin d\'accéder à l\'appareil photo de votre appareil."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activer"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidentialité du capteur"</string>
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 04b4236..eadac9e 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Só por wifi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Chamadas doutra SIM a través desta (<xliff:g id="SPN">%s</xliff:g>)"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Chamadas alternativas de <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: non desviada"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> tras <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Servizo de notificacións dos sensores"</string>
<string name="twilight_service" msgid="8964898045693187224">"Servizo Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detector de fuso horario (non require conexión)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Servizo de actualización horaria mediante o GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Borrarase o teu dispositivo"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Non se pode utilizar a aplicación de administración. Borrarase o teu dispositivo.\n\nSe tes preguntas, contacta co administrador da organización."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> desactivou a impresión."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Agora podes ampliar toda a pantalla ou parte dela"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activar en Configuración"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Ignorar"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> precisa acceder ao micrófono do dispositivo."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> precisa acceder á cámara do dispositivo."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activar"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidade do sensor"</string>
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 7bab2ad..86416bc 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ફક્ત વાઇ-ફાઇ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ક્રૉસ સિમ કૉલિંગ"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ફોરવર્ડ કર્યો નથી"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="TIME_DELAY">{2}</xliff:g> સેકન્ડ પછી <xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"સેન્સર નોટિફિકેશન સેવા"</string>
<string name="twilight_service" msgid="8964898045693187224">"ટ્વાઇલાઇટ સેવા"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"સમય ઝોન શોધવાની સુવિધા (કનેક્ટિવિટી જરૂરી નથી)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS સમય અપડેટ કરવાની સેવા"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"તમારું ઉપકરણ કાઢી નાખવામાં આવશે"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"વ્યવસ્થાપક ઍપનો ઉપયોગ કરી શકાશે નહીં. તમારું ઉપકરણ હવે કાઢી નાખવામાં આવશે.\n\nજો તમને પ્રશ્નો હોય, તો તમારી સંસ્થાના વ્યવસ્થાપકનો સંપર્ક કરો."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> દ્વારા પ્રિન્ટ કરવાનું બંધ કરાયું છે."</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"હવે તમે તમારી કેટલીક કે આખી સ્ક્રીનને મોટી કરી શકો છો"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"સેટિંગમાં ચાલુ કરો"</string>
<string name="dismiss_action" msgid="1728820550388704784">"છોડી દો"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"ચાલુ રાખવા માટે, <b><xliff:g id="APP">%s</xliff:g></b>ને તમારા ડિવાઇસના માઇક્રોફોનના ઍક્સેસની જરૂર છે."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ચાલુ રાખવા માટે, <b><xliff:g id="APP">%s</xliff:g></b>ને તમારા ડિવાઇસના કૅમેરાના ઍક્સેસની જરૂર છે."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ચાલુ કરો"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"સેન્સર પ્રાઇવસી સંબંધિત નોટિફિકેશન"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index dbdfd4a..5f0a8f8 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -57,7 +57,7 @@
<string name="imei" msgid="2157082351232630390">"आईएमईआई"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"इनकमिंग कॉलर आईडी"</string>
- <string name="ClirMmi" msgid="6752346475055446417">"आउटगोइंग कॉल करने पर अपना कॉलर आईडी छिपाएं"</string>
+ <string name="ClirMmi" msgid="6752346475055446417">"आउटगोइंग कॉल करने पर अपना नाम और नंबर छिपाएं"</string>
<string name="ColpMmi" msgid="4736462893284419302">"कनेक्ट किया गया लाइन आईडी"</string>
<string name="ColrMmi" msgid="5889782479745764278">"कनेक्ट किया गया लाइन आईडी प्रतिबंध"</string>
<string name="CfMmi" msgid="8390012691099787178">"कॉल आगे भेजना"</string>
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"केवल वाई-फ़ाई"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> क्रॉस सिम कॉलिंग"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> बैक अप कॉलिंग"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित नहीं किया गया"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंड के बाद"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"सेंसर से जुड़ी सूचना सेवा"</string>
<string name="twilight_service" msgid="8964898045693187224">"ट्वाइलाइट समय बताने वाली सेवा"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"समय क्षेत्र का पता लगाने वाली सुविधा (ऑफ़लाइन होने पर)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS समय अपडेट सेवा"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"आपके डिवाइस को मिटा दिया जाएगा"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"एडमिन ऐप्लिकेशन का इस्तेमाल नहीं किया जा सकता. आपके डिवाइस पर मौजूद डेटा अब मिटा दिया जाएगा.\n\nअगर आप कुछ पूछना चाहते हैं तो, अपने संगठन के एडमिन से संपर्क करें."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ने प्रिंटिंग सुविधा बंद कर दी है."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"अब अपनी पूरी स्क्रीन या कुछ हिस्से को ज़ूम करके देख सकते हैं"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"सेटिंग में जाकर, इस सुविधा को चालू करें"</string>
<string name="dismiss_action" msgid="1728820550388704784">"खारिज करें"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"जारी रखने के लिए, <b><xliff:g id="APP">%s</xliff:g></b> को आपके डिवाइस का माइक्रोफ़ोन ऐक्सेस करने की ज़रूरत है."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"जारी रखने के लिए, <b><xliff:g id="APP">%s</xliff:g></b> को आपके डिवाइस का कैमरा ऐक्सेस करने की ज़रूरत है."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"चालू करें"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेंसर से जुड़ी निजता के बारे में सूचना"</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 56f3978..e1ae1e2 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -149,7 +149,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Preusmjeravanje poziva na drugi SIM"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Rezervni način telefoniranja"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije proslijeđeno"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> nakon <xliff:g id="TIME_DELAY">{2}</xliff:g> s"</string>
@@ -205,8 +205,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Usluga Obavijesti senzora"</string>
<string name="twilight_service" msgid="8964898045693187224">"Usluga Sumrak"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detektor vremenske zone (nije povezan)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS – usluga ažuriranja vremena"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Uređaj će se izbrisati"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Administratorska aplikacija ne može se upotrebljavati. Uređaj će se izbrisati.\n\nAko imate pitanja, obratite se administratoru organizacije."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Ispis je onemogućila aplikacija <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2239,12 +2238,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Sad možete povećati dio zaslona ili cijeli zaslon"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Uključite u Postavkama"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Odbaci"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Da bi nastavila s radom, aplikacija <b><xliff:g id="APP">%s</xliff:g></b> treba pristupiti mikrofonu vašeg uređaja."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Da bi nastavila s radom, aplikacija <b><xliff:g id="APP">%s</xliff:g></b> treba pristupiti fotoaparatu vašeg uređaja."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Uključi"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatnost senzora"</string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 96997c0..3c04bec 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Csak Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM-eken keresztüli hívás"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> másodlagos hívási lehetőség"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nincs átirányítva"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> másodperc után"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Szenzoros értesítési szolgáltatás"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight szolgáltatás"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Időzóna-felismerő (Offline)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS időfrissítési szolgáltatás"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"A rendszer törölni fogja eszközét"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"A rendszergazdai alkalmazás nem használható. A rendszer most törli az eszközt.\n\nKérdéseivel forduljon szervezete rendszergazdájához."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"A(z) <xliff:g id="OWNER_APP">%s</xliff:g> letiltotta a nyomtatást."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Ezután nagyíthatja a képernyőt vagy egy részét"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Bekapcsolás a Beállításokban"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Elvetés"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"A folytatáshoz a(z) <b><xliff:g id="APP">%s</xliff:g></b> alkalmazásnak hozzáférésre van szüksége az eszköze mikrofonjához."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"A folytatáshoz a(z) <b><xliff:g id="APP">%s</xliff:g></b> alkalmazásnak hozzáférésre van szüksége az eszköze kamerájához."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Bekapcsolás"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Érzékelőkkel kapcsolatos adatvédelem"</string>
</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 996454a..bdd2387 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Միայն Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM քարտերով խաչաձև զանգեր"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> զանգելու պահեստային տարբերակ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. Չի վերահասցեավորվել"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> վայրկյանից"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Տվիչների ծանուցումների մշակման ծառայություն"</string>
<string name="twilight_service" msgid="8964898045693187224">"Մթնշաղի սկիզբը որոշող ծառայություն"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Ժամային գոտու դետեկտոր (աշխատում է առանց ինտերնետի)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Ժամանակի թարմացման GNSS ծառայություն"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Ձեր սարքը ջնջվելու է"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Հնարավոր չէ օգտագործել ադմինիստրատորի հավելվածը։ Ձեր սարքից բոլոր տվյալները կջնջվեն։\n\nՀարցեր ունենալու դեպքում դիմեք ձեր կազմակերպության ադմինիստրատորին։"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Տպումն անջատված է <xliff:g id="OWNER_APP">%s</xliff:g> հավելվածի կողմից։"</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Այժմ կարող եք խոշորացնել ամբողջ էկրանը կամ դրա մի մասը"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Միացնել կարգավորումներում"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Փակել"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Շարունակելու համար <b><xliff:g id="APP">%s</xliff:g></b> հավելվածին անհրաժեշտ է սարքի խոսափողի օգտագործման թույլտվություն։"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Շարունակելու համար <b><xliff:g id="APP">%s</xliff:g></b> հավելվածին անհրաժեշտ է ձեր սարքի տեսախցիկի օգտագործման թույլտվություն։"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Միացնել"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Տվիչների գաղտնիություն"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index bb91a6c..c84bc13 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Khusus Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Panggilan Lintas-SIM <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Panggilan Cadangan <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak diteruskan"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> setelah <xliff:g id="TIME_DELAY">{2}</xliff:g> detik"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Layanan Notifikasi Sensor"</string>
<string name="twilight_service" msgid="8964898045693187224">"Layanan Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Pendeteksi Zona Waktu (Tidak ada konektivitas)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Layanan Pembaruan Waktu GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Perangkat akan dihapus"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Aplikasi admin tidak dapat digunakan. Perangkat Anda kini akan dihapus.\n\nJika ada pertanyaan, hubungi admin organisasi."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Fitur pencetakan dinonaktifkan oleh <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Anda bisa memperbesar sebagian atau seluruh layar"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Aktifkan di Setelan"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Tutup"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Untuk melanjutkan, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses ke mikrofon perangkat."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Untuk melanjutkan, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses ke kamera perangkat."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktifkan"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privasi Sensor"</string>
</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index ec11ad2..84611a2 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi eingöngu"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Skipt á milli SIM-korta"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Varasímtöl (<xliff:g id="SPN">%s</xliff:g>)"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ekki áframsent"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> eftir <xliff:g id="TIME_DELAY">{2}</xliff:g> sekúndur"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Tilkynningaþjónusta nema"</string>
<string name="twilight_service" msgid="8964898045693187224">"Ljósaskiptaþjónusta"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Tímabeltisgreinir (engin tenging)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Tímastillingarþjónusta hnattræna gervihnattaleiðsögukerfisins (GNSS)"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Tækið verður hreinsað"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Ekki er hægt að nota stjórnunarforritið. Tækinu verður eytt.\n\nEf spurningar vakna skaltu hafa samband við kerfisstjóra fyrirtækisins."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> lokaði á prentun."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Nú geturðu stækkað allan skjáinn eða hluta hans"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Kveikja á í stillingum"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Hunsa"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Til að halda áfram þarf <b><xliff:g id="APP">%s</xliff:g></b> aðgang að hljóðnema tækisins."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Til að halda áfram þarf <b><xliff:g id="APP">%s</xliff:g></b> aðgang að myndavél tækisins."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Kveikja"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Persónuvernd skynjara"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index df35881..2fcc273 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -57,7 +57,7 @@
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"ID chiamante in entrata"</string>
- <string name="ClirMmi" msgid="6752346475055446417">"Nascondi ID chiamante in uscita"</string>
+ <string name="ClirMmi" msgid="6752346475055446417">"Nascondi ID chiamante per le chiamate in uscita"</string>
<string name="ColpMmi" msgid="4736462893284419302">"ID linea connessa"</string>
<string name="ColrMmi" msgid="5889782479745764278">"Limitazione ID linea connessa"</string>
<string name="CfMmi" msgid="8390012691099787178">"Deviazione chiamate"</string>
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Chiamate tramite più SIM <xliff:g id="SPN">%s</xliff:g>"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> dopo <xliff:g id="TIME_DELAY">{2}</xliff:g> secondi"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Servizio di notifica dei sensori"</string>
<string name="twilight_service" msgid="8964898045693187224">"Servizio Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Rilevatore di fuso orario (connessione a Internet non necessaria)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Servizio di aggiornamento dell\'orario GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Il dispositivo verrà resettato"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Impossibile usare l\'app di amministrazione. Il dispositivo verrà resettato.\n\nPer eventuali domande, contatta l\'amministratore della tua organizzazione."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Stampa disattivata da <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Puoi ingrandire lo schermo in parte o per intero"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Attiva nelle Impostazioni"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Ignora"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Per continuare, l\'app <b><xliff:g id="APP">%s</xliff:g></b> deve accedere al microfono del dispositivo."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Per continuare, l\'app <b><xliff:g id="APP">%s</xliff:g></b> deve accedere alla videocamera del dispositivo."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Attiva"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacy relativa ai sensori"</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index f80750a..f104d44 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -150,7 +150,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi בלבד"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"העברת שיחות בין כרטיסי SIM של <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"אמצעי גיבוי להתקשרות באמצעות <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ללא העברה"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> כעבור <xliff:g id="TIME_DELAY">{2}</xliff:g> שניות"</string>
@@ -207,8 +207,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"שירות להתראות מחיישנים"</string>
<string name="twilight_service" msgid="8964898045693187224">"שירות דמדומים"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"מזהה אזור זמן (ללא צורך בקישוריות)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"שירות עדכון הזמן של GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"תתבצע מחיקה של המכשיר"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"לא ניתן להשתמש באפליקציה של מנהל המערכת.\n\nאם יש לך שאלות, יש ליצור קשר עם מנהל המערכת של הארגון."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"ההדפסה הושבתה על ידי <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2273,12 +2272,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"עכשיו אפשר להגדיל את המסך או חלקים ממנו"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"הפעלה בהגדרות"</string>
<string name="dismiss_action" msgid="1728820550388704784">"סגירה"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"כדי להמשיך, האפליקציה <b><xliff:g id="APP">%s</xliff:g></b> צריכה גישה למיקרופון של המכשיר שלך."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"כדי להמשיך, האפליקציה <b><xliff:g id="APP">%s</xliff:g></b> צריכה גישה למצלמה של המכשיר שלך."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"הפעלה"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"פרטיות חיישנים"</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 04a0058..69822ac 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fiのみ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM 通話"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> 通話のバックアップ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:転送できません"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g> (<xliff:g id="TIME_DELAY">{2}</xliff:g>秒後)"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"センサー通知サービス"</string>
<string name="twilight_service" msgid="8964898045693187224">"トワイライト サービス"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Time Zone Detector(未接続)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS Time Update Service"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"デバイスのデータが消去されます"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"管理アプリを使用できません。デバイスのデータはこれから消去されます。\n\nご不明な点がある場合は、組織の管理者にお問い合わせください。"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"「<xliff:g id="OWNER_APP">%s</xliff:g>」により印刷は無効にされています。"</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"画面の一部または全体を拡大できるようになりました"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"[設定] で ON にする"</string>
<string name="dismiss_action" msgid="1728820550388704784">"閉じる"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"続行するには、<b><xliff:g id="APP">%s</xliff:g></b> にデバイスのマイクへのアクセスを許可する必要があります。"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"続行するには、<b><xliff:g id="APP">%s</xliff:g></b> にデバイスのカメラへのアクセスを許可する必要があります。"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ON にする"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"センサー プライバシー"</string>
</resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 6a128ab..6a7d563 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"მხოლოდ Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM-თაშორისი დარეკვა"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> დარეკვის სარეზერვო ხერხი"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: არ არის გადამისამართებული"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> წამის შემდეგ"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"სენსორის შეტყობინების სერვისი"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight სერვისი"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"სასაათო სარტყლის დეტექტორი (კავშირის გარეშე)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS დროის განახლების სერვისი"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"თქვენი მოწყობილობა წაიშლება"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"ადმინისტრატორის აპის გამოყენება ვერ მოხერხდება. თქვენი მოწყობილობა ახლა ამოიშლება.\n\nთუ შეკითხვები გაქვთ, დაუკავშირდით თქვენი ორგანიზაციის ადმინისტრატორს."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"ბეჭდვა გათიშულია <xliff:g id="OWNER_APP">%s</xliff:g>-ის მიერ."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"ახლა შეგიძლიათ, გაადიდოთ ეკრანი ან მისი ნაწილი"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ჩართვა პარამეტრებში"</string>
<string name="dismiss_action" msgid="1728820550388704784">"უარყოფა"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"გასაგრძელებლად <b><xliff:g id="APP">%s</xliff:g></b>-ს თქვენი მოწყობილობის მიკროფონზე წვდომა სჭირდება."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"გასაგრძელებლად <b><xliff:g id="APP">%s</xliff:g></b>-ს თქვენი მოწყობილობის კამერაზე წვდომა სჭირდება."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ჩართვა"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"სენსორის კონფიდენციალურობა"</string>
</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 8000fc4..aec2312 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Тек Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM карталары арасында қоңырау шалу"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Басқа нөмірге бағытталмады"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундтан кейін"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Датчик хабарландыруы қызметі"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight қызметі"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Уақыт белдеуін анықтағыш (қосылу мүмкіндігі жоқ)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS уақыт жаңарту жүйесі"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Құрылғыңыздағы деректер өшіріледі"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Әкімші қолданбасын пайдалану мүмкін емес. Қазір құрылғыдағы деректер өшіріледі\n\nСұрақтарыңыз болса, ұйым әкімшісіне хабарласыңыз."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Басып шығаруды <xliff:g id="OWNER_APP">%s</xliff:g> өшірді."</string>
@@ -1809,8 +1809,8 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"Жарайды"</string>
<string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батарея жұмысының ұзақтығын арттыру үшін Батареяны үнемдеу режимі:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді не шектейді.\n\n"<annotation id="url">"Толығырақ"</annotation></string>
<string name="battery_saver_description" msgid="6794188153647295212">"Батарея ұзағырақ жұмыс істеуі үшін, Battery Saver:\n\n• қараңғы тақырыпты қосады;\n•фондық жұмысты, кейбір визуалды әсерлерді және \"Ok Google\" сияқты басқа функцияларды өшіреді не шектейді."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Дерек шығынын азайту үшін Data Saver функциясы кейбір қолданбаларға деректерді фондық режимде жіберуге және алуға жол бермейді. Ашық тұрған қолданба деректерді пайдаланады, бірақ шектеулі шамада (мысалы, кескіндер оларды түрткенге дейін көрсетілмейді)."</string>
- <string name="data_saver_enable_title" msgid="7080620065745260137">"Data Saver функциясын қосу керек пе?"</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Дерек шығынын азайту үшін Трафикті үнемдеу функциясы кейбір қолданбаларға деректерді фондық режимде жіберуге және алуға жол бермейді. Ашық тұрған қолданба деректерді пайдаланады, бірақ шектеулі шамада (мысалы, кескіндер оларды түрткенге дейін көрсетілмейді)."</string>
+ <string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикті үнемдеу функциясын қосу керек пе?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Қосу"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273">
<item quantity="other">%1$d минут бойы (<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g> дейін)</item>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Енді экранның бір бөлігін не барлығын ұлғайта аласыз."</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Параметрлер бөлімінен қосу"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Қабылдамау"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Жалғастыру үшін <b><xliff:g id="APP">%s</xliff:g></b> қолданбасы құрылғыңыздың микрофонына рұқсат алу керек."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Жалғастыру үшін <b><xliff:g id="APP">%s</xliff:g></b> қолданбасы құрылғыңыздың камерасына рұқсат алу керек."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Қосу"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Датчикке қатысты құпиялылық"</string>
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index e6a2fc6..d1400b9 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi តែប៉ុណ្ណោះ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"ការហៅទូរសព្ទឆ្លងស៊ីមតាមរយៈ <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"ការហៅទូរសព្ទបម្រុង <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិនបានបញ្ជូនបន្ត"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> បន្ទាប់ពី <xliff:g id="TIME_DELAY">{2}</xliff:g> វិនាទី"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"សេវាកម្មជូនដំណឹងឧបករណ៍ចាប់សញ្ញា"</string>
<string name="twilight_service" msgid="8964898045693187224">"សេវាកម្មព្រលប់"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"ឧបករណ៍សម្គាល់ល្វែងម៉ោង (គ្មានការតភ្ជាប់ទេ)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"សេវាកម្មធ្វើបច្ចុប្បន្នភាពពេលវេលា GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"ឧបករណ៍របស់អ្នកនឹងត្រូវបានលុប"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"មិនអាចប្រើកម្មវិធីអ្នកគ្រប់គ្រងបានទេ។ ឧបករណ៍របស់អ្នកនឹងលុបឥឡូវនេះ។\n\nប្រសិនបើអ្នកមានសំណួរផ្សេងៗ សូមទាក់ទងទៅអ្នកគ្រប់គ្រងស្ថាប័នរបស់អ្នក។"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"ការបោះពុម្ពត្រូវបានបិទដោយ <xliff:g id="OWNER_APP">%s</xliff:g> ។"</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"ឥឡូវនេះ អ្នកអាចពង្រីកផ្នែកខ្លះ ឬទាំងអស់នៃអេក្រង់របស់អ្នក"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"បើកនៅក្នុងការកំណត់"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ច្រានចោល"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"ដើម្បីបន្ត <b><xliff:g id="APP">%s</xliff:g></b> ត្រូវការសិទ្ធិចូលប្រើមីក្រូហ្វូនរបស់ឧបករណ៍អ្នក។"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ដើម្បីបន្ត <b><xliff:g id="APP">%s</xliff:g></b> ត្រូវការសិទ្ធិចូលប្រើកាមេរ៉ារបស់ឧបករណ៍អ្នក។"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"បើក"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ឯកជនភាពឧបករណ៍ចាប់សញ្ញា"</string>
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index f2d91f2..32e3c4d 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ವೈ-ಫೈ ಮಾತ್ರ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ಕ್ರಾಸ್-ಸಿಮ್ ಕರೆ ಮಾಡುವಿಕೆ"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ಬ್ಯಾಕಪ್ ಕರೆ ಮಾಡುವಿಕೆ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ಫಾರ್ವರ್ಡ್ ಮಾಡಲಾಗಿಲ್ಲ"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"ಸೆನ್ಸರ್ ಅಧಿಸೂಚನೆ ಸೇವೆ"</string>
<string name="twilight_service" msgid="8964898045693187224">"ಟ್ವಿಲೈಟ್ ಸೇವೆ"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"ಸಮಯವಲಯ ಡಿಟೆಕ್ಟರ್ (ಯಾವುದೇ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆ ಇಲ್ಲ)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS ಸಮಯದ ಅಪ್ಡೇಟ್ ಸೇವೆ"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"ನಿರ್ವಹಣೆ ಅಪ್ಲಿಕೇಶನ್ ಬಳಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ನಿಮ್ಮ ಸಾಧನವನ್ನು ಇದೀಗ ಅಳಿಸಲಾಗುತ್ತದೆ.\n\nನಿಮ್ಮಲ್ಲಿ ಪ್ರಶ್ನೆಗಳಿದ್ದರೆ, ನಿಮ್ಮ ಸಂಸ್ಥೆಯ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ಮೂಲಕ ಪ್ರಿಂಟಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"ಈಗ ಕೆಲವು ಅಥವಾ ಎಲ್ಲಾ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಹಿಗ್ಗಿಸಬಹುದು"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಆನ್ ಮಾಡಿ"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ವಜಾಗೊಳಿಸಿ"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"ಮುಂದುವರಿಯಲು, <b><xliff:g id="APP">%s</xliff:g></b> ಗೆ ನಿಮ್ಮ ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ನ ಪ್ರವೇಶದ ಅಗತ್ಯವಿದೆ."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ಮುಂದುವರಿಯಲು, <b><xliff:g id="APP">%s</xliff:g></b> ಗೆ ನಿಮ್ಮ ಸಾಧನದ ಕ್ಯಾಮರಾದ ಪ್ರವೇಶದ ಅಗತ್ಯವಿದೆ."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ಆನ್ ಮಾಡಿ"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ಸೆನ್ಸರ್ ಗೌಪ್ಯತೆ"</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index ea5de596..3407431 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi에서만"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross SIM 통화"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> 백업 전화"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안됨"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g><xliff:g id="TIME_DELAY">{2}</xliff:g>초 후"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"센서 알림 서비스"</string>
<string name="twilight_service" msgid="8964898045693187224">"새벽 서비스"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"시간대 감지(연결되지 않음)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS 시간 업데이트 서비스"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"기기가 삭제됩니다."</string>
<string name="factory_reset_message" msgid="2657049595153992213">"관리자 앱을 사용할 수 없습니다. 곧 기기가 삭제됩니다.\n\n궁금한 점이 있으면 조직의 관리자에게 문의하세요."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g>에 의해 사용 중지되었습니다."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"이제 화면 일부 또는 전체를 확대할 수 있습니다."</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"설정에서 사용 설정"</string>
<string name="dismiss_action" msgid="1728820550388704784">"닫기"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"계속하려면 <b><xliff:g id="APP">%s</xliff:g></b>에서 기기 마이크에 액세스해야 합니다."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"계속하려면 <b><xliff:g id="APP">%s</xliff:g></b>에서 기기 카메라에 액세스해야 합니다."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"사용"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"센서 개인정보 보호"</string>
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 5137a22..9a84d0a 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi гана"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM карталарынан кайчылаш чалуу"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Кошумча чалуу ыкмасы"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Багытталган эмес"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секунддан кийин"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Сенсордун билдирмелеринин кызматы"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight кызматы"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Убакыт алкагын аныктагыч (байланыш жок)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS Убакытты жаңыртуу кызматы"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Түзмөгүңүз тазаланат"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Түзмөктү башкаруучу колдонмо жараксыз. Түзмөгүңүз азыр тазаланат.\n\nСуроолоруңуз болсо, ишканаңыздын администраторуна кайрылыңыз."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Басып чыгаруу <xliff:g id="OWNER_APP">%s</xliff:g> тарабынан өчүрүлдү."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Эми толук экранды же анын бөлүгүн чоңойто аласыз"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Жөндөөлөрдөн күйгүзүү"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Жабуу"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Улантуу үчүн <b><xliff:g id="APP">%s</xliff:g></b> колдонмосуна түзмөгүңүздүн микрофонун пайдаланууга уруксат беришиңиз керек."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Улантуу үчүн <b><xliff:g id="APP">%s</xliff:g></b> колдонмосуна түзмөгүңүздүн камерасын пайдаланууга уруксат беришиңиз керек."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Күйгүзүү"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Сенсордун купуялыгы"</string>
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 7de2a91..9977f9f 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi ເທົ່ານັ້ນ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ການໂທຂ້າມຊິມ"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ບໍ່ຖືກສົ່ງຕໍ່"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ຫຼັງຈາກ <xliff:g id="TIME_DELAY">{2}</xliff:g> ວິນາທີ"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"ບໍລິການການແຈ້ງເຕືອນເຊັນເຊີ"</string>
<string name="twilight_service" msgid="8964898045693187224">"ບໍລິການ Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"ຕົວກວດຫາເຂດເວລາ (ບໍ່ມີການເຊື່ອມຕໍ່)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"ບໍລິການອັບເດດເວລາ GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"ອຸປະກອນຂອງທ່ານຈະຖືກລຶບ"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"ບໍ່ສາມາດໃຊ້ແອັບຜູ້ເບິ່ງແຍງລະບົບໄດ້. ອຸປະກອນຂອງທ່ານຈະຖືກລຶບຂໍ້ມູນໃນຕອນນີ້.\n\nຫາກທ່ານມີຄຳຖາມ, ໃຫ້ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບອົງກອນຂອງທ່ານ."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"ການພິມຖືກປິດໄວ້ໂດຍ <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"ຕອນນີ້ທ່ານສາມາດຂະຫຍາຍບາງສ່ວນ ຫຼື ທັງໝົດຂອງໜ້າຈໍໄດ້"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ເປີດໃຊ້ໃນການຕັ້ງຄ່າ"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ປິດໄວ້"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"ເພື່ອດຳເນີນການຕໍ່, <b><xliff:g id="APP">%s</xliff:g></b> ຕ້ອງການສິດເຂົ້າເຖິງໄມໂຄຣໂຟນອຸປະກອນທ່ານ."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ເພື່ອດຳເນີນການຕໍ່, <b><xliff:g id="APP">%s</xliff:g></b> ຕ້ອງການສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຂອງອຸປະກອນທ່ານ."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ເປີດໃຊ້"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ຄວາມເປັນສ່ວນຕົວເຊັນເຊີ"</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 155d4cfb..5f72e07 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -150,7 +150,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Tik „Wi-Fi“"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"„<xliff:g id="SPN">%s</xliff:g>“: skambinimas per SIM korteles"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> – atsarginis skambinimas"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neperadresuota"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sek."</string>
@@ -207,8 +207,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Jutiklių pranešimų paslauga"</string>
<string name="twilight_service" msgid="8964898045693187224">"Paslauga „Twilight“"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Laiko juostos aptikimo priemonė (nėra ryšio)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS laiko atnaujinimo paslauga"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Įrenginys bus ištrintas"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Administratoriaus programos negalima naudoti. Dabar įrenginio duomenys bus ištrinti.\n\nJei turite klausimų, susisiekite su organizacijos administratoriumi."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Neleidžiama spausdinti (<xliff:g id="OWNER_APP">%s</xliff:g>)."</string>
@@ -2273,12 +2272,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Dabar galite padidinti dalį ekrano ar jį visą"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Įjungti nustatymuose"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Atmesti"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Kad būtų galima tęsti, <b><xliff:g id="APP">%s</xliff:g></b> reikalinga prieiga prie įrenginio mikrofono."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Kad būtų galima tęsti, <b><xliff:g id="APP">%s</xliff:g></b> reikalinga prieiga prie įrenginio fotoaparato."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Įjungti"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Jutiklių privatumas"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index cd88997..37e417f 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -149,7 +149,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Tikai Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g>: zvanīšana, izmantojot dažādas SIM kartes"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>: zvanu rezerves iespēja"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nav pāradresēts"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pēc <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundes(-ēm)"</string>
@@ -205,8 +205,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Sensoru paziņojumu pakalpojums"</string>
<string name="twilight_service" msgid="8964898045693187224">"Krēslas noteikšanas pakalpojums"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Laika joslas noteikšanas rīks (nav savienojuma)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS laika atjaunināšanas pakalpojums"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Jūsu ierīces dati tiks dzēsti"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Administratora lietotni nevar izmantot. Ierīcē saglabātie dati tiks dzēsti.\n\nJa jums ir kādi jautājumi, sazinieties ar savas organizācijas administratoru."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Drukāšanu atspējoja <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2239,12 +2238,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Tagad varat palielināt ekrāna daļu vai visu ekrānu"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ieslēgt sadaļā Iestatījumi"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Nerādīt"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Lai turpinātu, lietotnei <b><xliff:g id="APP">%s</xliff:g></b> nepieciešama piekļuve jūsu ierīces mikrofonam."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Lai turpinātu, lietotnei <b><xliff:g id="APP">%s</xliff:g></b> nepieciešama piekļuve jūsu ierīces kamerai."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Ieslēgt"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensoru konfidencialitāte"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc950-si/strings.xml b/core/res/res/values-mcc310-mnc950-si/strings.xml
deleted file mode 100644
index 26fe4ac..0000000
--- a/core/res/res/values-mcc310-mnc950-si/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="615419724607901560">"SIM MM#2 ප්රතිපාදනය නොකරයි"</string>
- <string name="mmcc_illegal_ms" msgid="7801541624846497489">"SIM MM#3 ඉඩ නොදේ"</string>
- <string name="mmcc_illegal_me" msgid="7066936962628406316">"දුරකථනය MM#6 ඉඩ නොදේ"</string>
-</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index d8ecc54..5666cc2 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Повици преку повеќе SIM-картички на <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Резервен начин на повикување преку <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не е препратено"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> по <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Услуга за известување од сензорот"</string>
<string name="twilight_service" msgid="8964898045693187224">"Услуга за самрак"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Откривач на временска зона (не може да се поврзе)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Услуга за ажурирање на времето на GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Уредот ќе се избрише"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Апликацијата на администраторот не може да се користи. Уредот ќе се избрише сега.\n\nАко имате прашања, контактирајте со администраторот на организацијата."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Печатењето е оневозможено од <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Сега може се зголеми целиот екран или само дел"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Вклучи во „Поставки“"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Отфрли"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"За да продолжи, на <b><xliff:g id="APP">%s</xliff:g></b> ѝ е потребен пристап до микрофонот на уредот."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"За да продолжи, на <b><xliff:g id="APP">%s</xliff:g></b> ѝ е потребен пристап до камерата на уредот."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Вклучи"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Приватност на сензорот"</string>
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index f7a3161..e0d4b01 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"വൈഫൈ മാത്രം"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ക്രോസ് സിം കോളിംഗ്"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: കൈമാറിയില്ല"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> നിമിഷത്തിനുശേഷം <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"സെൻസർ അറിയിപ്പ് സേവനം"</string>
<string name="twilight_service" msgid="8964898045693187224">"സന്ധ്യാസമയത്തെ സേവനം"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"സമയമേഖല കണ്ടെത്താനുള്ള സംവിധാനം (കണക്റ്റിവിറ്റി ഇല്ല)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS സമയ അപ്ഡേറ്റ് സേവനം"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"നിങ്ങളുടെ ഉപകരണം മായ്ക്കും"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"അഡ്മിൻ ആപ്പ് ഉപയോഗിക്കാനാകില്ല. നിങ്ങളുടെ ഉപകരണം ഇപ്പോൾ മായ്ക്കപ്പെടും.\n\nനിങ്ങൾക്ക് ചോദ്യങ്ങൾ ഉണ്ടെങ്കിൽ, നിങ്ങളുടെ സ്ഥാപനത്തിന്റെ അഡ്മിനെ ബന്ധപ്പെടുക."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> പ്രിന്റിംഗ് പ്രവർത്തനരഹിതമാക്കി."</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"സ്ക്രീനിന്റെ ഭാഗങ്ങളോ മുഴുവനുമോ മാഗ്നിഫൈ ചെയ്യാം"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ക്രമീകരണത്തിൽ ഓണാക്കുക"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ഡിസ്മിസ് ചെയ്യുക"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"തുടരാൻ, <b><xliff:g id="APP">%s</xliff:g></b> ആപ്പിന് നിങ്ങളുടെ ഉപകരണത്തിന്റെ മൈക്രോഫോണിലേക്ക് ആക്സസ് നൽകേണ്ടതുണ്ട്."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"തുടരാൻ, <b><xliff:g id="APP">%s</xliff:g></b> ആപ്പിന് നിങ്ങളുടെ ഉപകരണത്തിന്റെ ക്യാമറയിലേക്ക് ആക്സസ് നൽകേണ്ടതുണ്ട്."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ഓണാക്കുക"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"സെൻസർ സ്വകാര്യത"</string>
</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index cb3f00d..891ac47 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Зөвхөн Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> SIM хоорондын дуудлага"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Дуудлагыг нөөцлөх"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: дамжуулагдаагүй"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундын дараа"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Мэдрэгчийн мэдэгдлийн үйлчилгээ"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight үйлчилгээ"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Цагийн бүс илрүүлэгч (Холболт байхгүй)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS Хугацаа шинэчлэлтийн үйлчилгээ"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Таны төхөөрөмж устах болно."</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Админ аппыг ашиглах боломжгүй. Таны төхөөрөмжийг одоо устгана.\n\nХэрэв танд асуулт байгаа бол байгууллагынхаа админтай холбогдоно уу."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> хэвлэх үйлдлийг идэвхгүй болгосон."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Та одоо зарим эсвэл бүх дэлгэцээ томруулж болно"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Тохиргоонд асаана уу"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Үл хэрэгсэх"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Үргэлжлүүлэхийн тулд, <b><xliff:g id="APP">%s</xliff:g></b> таны төхөөрөмжийн микрофонд хандах шаардлагатай."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Үргэлжлүүлэхийн тулд <b><xliff:g id="APP">%s</xliff:g></b> таны төхөөрөмжийн камерт хандах шаардлагатай."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Асаах"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Мэдрэгчийн нууцлал"</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 4921f25..d57c4c1 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -57,8 +57,7 @@
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"येणारा कॉलर आयडी"</string>
- <!-- no translation found for ClirMmi (6752346475055446417) -->
- <skip />
+ <string name="ClirMmi" msgid="6752346475055446417">"आउटगोइंग कॉलर आयडी लपवा"</string>
<string name="ColpMmi" msgid="4736462893284419302">"कनेक्ट केलेला रेखा आयडी"</string>
<string name="ColrMmi" msgid="5889782479745764278">"कनेक्ट केलेला रेखा आयडी प्रतिबंध"</string>
<string name="CfMmi" msgid="8390012691099787178">"कॉल फॉरवर्डिंग"</string>
@@ -149,12 +148,13 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"केवळ वाय-फाय"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> क्रॉस सिम कॉलिंग"</string>
- <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित केला नाही"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
+ <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: फॉरवर्ड केला नाही"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंदांनंतर <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
- <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित केला नाही"</string>
- <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित केला नाही"</string>
+ <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: फॉरवर्ड केला नाही"</string>
+ <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: फॉरवर्ड केला नाही"</string>
<string name="fcComplete" msgid="1080909484660507044">"वैशिष्ट्य कोड पूर्ण."</string>
<string name="fcError" msgid="5325116502080221346">"कनेक्शन समस्या किंवा अवैध फीचर कोड."</string>
<string name="httpErrorOk" msgid="6206751415788256357">"ठीक"</string>
@@ -204,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"सेन्सर सूचना सेवा"</string>
<string name="twilight_service" msgid="8964898045693187224">"ट्वायलाइट सेवा"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"टाइम झोन डिटेक्टर (कनेक्टिव्हिटी नाही)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS ची वेळ अपडेट करणारी सेवा"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"तुमचे डिव्हाइस मिटविले जाईल"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"प्रशासक अॅप वापरता येणार नाही. तुमचे डिव्हाइस आता साफ केले जाईल.\n\nतुम्हाला कुठलेही प्रश्न असल्यास, तुमच्या संस्थेच्या प्रशासकाशी संपर्क साधा."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> नी प्रिंट करणे बंद केले आहे."</string>
@@ -2206,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"आता स्क्रीन अंशतः किंवा पूर्ण मॅग्निफाय करू शकता"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"सेटिंग्ज मध्ये सुरू करा"</string>
<string name="dismiss_action" msgid="1728820550388704784">"डिसमिस करा"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"पुढे सुरू ठेवण्यासाठी, <b><xliff:g id="APP">%s</xliff:g></b> ला तुमच्या डिव्हाइसचा मायक्रोफोन अॅक्सेस करण्याची आवश्यकता आहे."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"पुढे सुरू ठेवण्यासाठी, <b><xliff:g id="APP">%s</xliff:g></b> ला तुमच्या डिव्हाइसचा कॅमेरा अॅक्सेस करण्याची आवश्यकता आहे."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"सुरू करा"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेन्सरशी संबंधित गोपनीयतेबाबत सूचना"</string>
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 557d64d..ec0aa5d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi sahaja"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Panggilan Silang Sim"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Panggilan Sandaran"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak dimajukan"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> selepas <xliff:g id="TIME_DELAY">{2}</xliff:g> saat"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Perkhidmatan Pemberitahuan Penderia"</string>
<string name="twilight_service" msgid="8964898045693187224">"Perkhidmatan Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Pengesan Zon Waktu (Tiada kesambungan)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Perkhidmatan Kemaskinian Waktu GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Peranti anda akan dipadam"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Apl pentadbir tidak dapat digunakan. Peranti anda akan dipadamkan sekarang.\n\nJika anda ingin mengemukakan soalan, hubungi pentadbir organisasi anda."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Pencetakan dilumpuhkan oleh <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Besarkan sebahagian atau keseluruhan skrin anda"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Hidupkan dalam Tetapan"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Tolak"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Untuk meneruskan proses, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses kepada mikrofon peranti anda."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Untuk meneruskan proses, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses kepada kamera peranti anda."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Hidupkan"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privasi Penderia"</string>
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 49387d8..7b4a430fc 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ကြိုးမဲ့အင်တာနက် သာလျှင်"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Cross-SIM ခေါ်ဆိုမှု"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> အရန် ခေါ်ဆိုမှု"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ထပ်ဆင့်မပို့နိုင်ပါ"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> နောက် <xliff:g id="TIME_DELAY">{2}</xliff:g> စက္ကန့်"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"အာရုံခံကိရိယာ အကြောင်းကြားချက် ဝန်ဆောင်မှု"</string>
<string name="twilight_service" msgid="8964898045693187224">"နေဝင်ဆည်းဆာ ဝန်ဆောင်မှု"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"ဒေသစံတော်ချိန် ရှာဖွေစနစ် (ချိတ်ဆက်နိုင်မှု မလိုပါ)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS အချိန်အပ်ဒိတ် ဝန်ဆောင်မှု"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"သင့်ကိရိယာအား ပယ်ဖျက်လိမ့်မည်"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"စီမံခန့်ခွဲမှု အက်ပ်ကို သုံး၍မရပါ။ သင်၏ စက်ပစ္စည်းအတွင်းရှိ အရာများကို ဖျက်လိုက်ပါမည်\n\nမေးစရာများရှိပါက သင့်အဖွဲ့အစည်း၏ စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> က ပုံနှိပ်ထုတ်ယူခြင်းကို ပိတ်ထားသည်။"</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"ဖန်သားပြင် တစ်စိတ်တစ်ပိုင်း (သို့) တစ်ခုလုံး ချဲ့နိုင်ပါပြီ"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"\'ဆက်တင်များ\' တွင် ဖွင့်ရန်"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ပယ်ရန်"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"ဆက်လက်လုပ်ဆောင်ရန် <b><xliff:g id="APP">%s</xliff:g></b> က သင့်စက်၏ မိုက်ခရိုဖုန်းကို အသုံးပြုခွင့်ရရန် လိုအပ်သည်။"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ဆက်လက်လုပ်ဆောင်ရန် <b><xliff:g id="APP">%s</xliff:g></b> က သင့်စက်၏ ကင်မရာကို အသုံးပြုခွင့်ရရန် လိုအပ်သည်။"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ဖွင့်ရန်"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"အာရုံခံကိရိယာ လုံခြုံရေး"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 2be1dab..4968184 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Bare Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Ringing mellom SIM-kort med <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>-reserve for anrop"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderekoblet"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> etter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Sensor Notification Service"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight Service"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Tidssoneoppdagelse (ingen tilkobling)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS-tjeneste for tidsoppdatering"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Enheten blir slettet"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Administratorappen kan ikke brukes. Enheten din blir nå tømt.\n\nTa kontakt med administratoren for organisasjonen din hvis du har spørsmål."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> har slått av utskrift."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Nå kan du forstørre deler av eller hele skjermen"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Slå på i innstillingene"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Avvis"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"For å fortsette må <b><xliff:g id="APP">%s</xliff:g></b> ha tilgang til enhetsmikrofonen."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"For å fortsette må <b><xliff:g id="APP">%s</xliff:g></b> ha tilgang til enhetskameraet."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Slå på"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorpersonvern"</string>
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index b309e4a..541ace8 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi मात्र"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> क्रस SIM कलिङ"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अगाडि पठाइएको छैन"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> पछि <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकेन्ड"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"सेन्सरको सूचनासम्बन्धी सेवा"</string>
<string name="twilight_service" msgid="8964898045693187224">"ट्वाइलाइट सेवा"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"समय क्षेत्र पत्ता लगाउने सुविधा (नेटवर्क कनेक्सन नहुँदा)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS को समय अपडेट गर्ने सेवा"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"तपाईंको यन्त्र मेटिनेछ"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"प्रशासकको एप प्रयोग गर्न मिल्दैन। तपाईंको यन्त्रको डेटा अब मेटाइने छ।\n\nतपाईंसँग प्रश्नहरू भएका खण्डमा आफ्नो संगठनका प्रशासकसँग सम्पर्क गर्नुहोस्।"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ले छाप्ने कार्यलाई असक्षम पार्यो।"</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"तपाईं अब स्क्रिनको केही वा सबै भाग जुम इन गर्न सक्नुहुन्छ"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"सेटिङमा गई यो सुविधा अन गर्नुहोस्"</string>
<string name="dismiss_action" msgid="1728820550388704784">"हटाउनुहोस्"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"जारी राख्न <b><xliff:g id="APP">%s</xliff:g></b> लाई तपाईंको यन्त्रको माइक्रोफोन प्रयोग गर्ने अनुमति दिनु पर्ने हुन्छ।"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"जारी राख्न <b><xliff:g id="APP">%s</xliff:g></b> लाई तपाईंको यन्त्रको क्यामेरा प्रयोग गर्ने अनुमति दिनु पर्ने हुन्छ।"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"अन गर्नुहोस्"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेन्सरसम्बन्धी गोपनीयता"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c8621a1..7f23cbb 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Alleen wifi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Bellen met meerdere simkaarten van <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Reserveoptie voor bellen van <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: niet doorgeschakeld"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> na <xliff:g id="TIME_DELAY">{2}</xliff:g> seconden"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Service voor sensormeldingen"</string>
<string name="twilight_service" msgid="8964898045693187224">"Service voor schemering"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Tijdzonedetector (Geen verbinding)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Updateservice voor GNSS-tijd"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Je apparaat wordt gewist"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"De beheer-app kan niet worden gebruikt. Je apparaat wordt nu gewist.\n\nNeem contact op met de beheerder van je organisatie als je vragen hebt."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Afdrukken uitgeschakeld door <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Je kunt je scherm nu (gedeeltelijk) vergroten"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Inschakelen in Instellingen"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Sluiten"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"<b><xliff:g id="APP">%s</xliff:g></b> heeft toegang tot de microfoon van je apparaat nodig om door te gaan."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> heeft toegang tot de camera van je apparaat nodig om door te gaan."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aanzetten"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorprivacy"</string>
</resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 6e07d75..f02d157 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"କେବଳ ୱାଇ-ଫାଇ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> କ୍ରସ୍ SIM କଲିଂ"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ଫରୱାର୍ଡ କରାଯାଇନାହିଁ"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ସେକେଣ୍ଡ ପରେ"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"ସେନ୍ସର୍ ନୋଟିଫିକେସନ୍ ସର୍ଭିସ୍"</string>
<string name="twilight_service" msgid="8964898045693187224">"ଟ୍ୱିଲାଇଟ୍ ସର୍ଭିସ୍"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"ଟାଇମ୍ ଜୋନ୍ ଡିଟେକ୍ଟର୍ (କୌଣସି ସଂଯୋଗ ନାହିଁ)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS ସମୟ ଅପଡେଟ୍ ସେବା"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"ଆପଣଙ୍କ ଡିଭାଇସ୍ ବର୍ତ୍ତମାନ ଲିଭାଯିବ"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"ଆଡମିନ୍ ଆପ୍ ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ। ଆପଣଙ୍କ ଡିଭାଇସ୍ର ସମସ୍ତ ଡାଟାକୁ ବର୍ତ୍ତମାନ ଲିଭାଇଦିଆଯିବ। \n\nଯଦି ଆପଣଙ୍କର କୌଣସି ପ୍ରଶ୍ନ ରହିଥାଏ, ଆପଣଙ୍କ ସଂସ୍ଥାର ଆଡମିନ୍ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ଦ୍ଵାରା ପ୍ରିଣ୍ଟିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି"</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"ଆପଣ ଏବେ ଆଂଶିକ ବା ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ ବଡ଼ କରିପାରିବେ"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ସେଟିଂସରେ ଚାଲୁ କରନ୍ତୁ"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ଖାରଜ କରନ୍ତୁ"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"ଜାରି ରଖିବାକୁ, <b><xliff:g id="APP">%s</xliff:g></b> ଆପଣଙ୍କ ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଆକ୍ସେସ୍ ଆବଶ୍ୟକ କରେ।"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ଜାରି ରଖିବାକୁ, <b><xliff:g id="APP">%s</xliff:g></b> ଆପଣଙ୍କ ଡିଭାଇସର କ୍ୟାମେରାକୁ ଆକ୍ସେସ୍ ଆବଶ୍ୟକ କରେ।"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ଚାଲୁ କରନ୍ତୁ"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ସେନ୍ସର୍ ଗୋପନୀୟତା"</string>
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 52e124c..a717af6 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -57,8 +57,7 @@
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"ਇਨਕਮਿੰਗ ਕਾਲਰ ਆਈ.ਡੀ."</string>
- <!-- no translation found for ClirMmi (6752346475055446417) -->
- <skip />
+ <string name="ClirMmi" msgid="6752346475055446417">"ਆਊਟਗੋਇੰਗ ਕਾਲਰ ਆਈਡੀ ਲੁਕਾਓ"</string>
<string name="ColpMmi" msgid="4736462893284419302">"ਕਨੈਕਟ ਕੀਤੀ ਲਾਈਨ ਆਈ.ਡੀ."</string>
<string name="ColrMmi" msgid="5889782479745764278">"ਕਨੈਕਟ ਕੀਤੀ ਲਾਈਨ ਆਈ.ਡੀ. ਪ੍ਰਤਿਬੰਧ"</string>
<string name="CfMmi" msgid="8390012691099787178">"ਕਾਲ ਫਾਰਵਰਡਿੰਗ"</string>
@@ -149,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ਸਿਰਫ਼ ਵਾਈ-ਫਾਈ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> ਕ੍ਰਾਸ-ਸਿਮ ਕਾਲਿੰਗ"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ਅੱਗੇ ਨਹੀਂ ਭੇਜਿਆ ਗਿਆ"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ਸਕਿੰਟਾਂ ਬਾਅਦ"</string>
@@ -204,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"ਸੈਂਸਰ ਸੂਚਨਾ ਸੇਵਾ"</string>
<string name="twilight_service" msgid="8964898045693187224">"ਟਵੀਲਾਈਟ ਸੇਵਾ"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"ਸਮਾਂ ਖੇਤਰ ਦਾ ਪਤਾ ਲਗਾਉਣ ਦੀ ਸੁਵਿਧਾ (ਕੋਈ ਕਨੈਕਟੀਵਿਟੀ ਨਹੀਂ)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS ਸਮਾਂ ਅੱਪਡੇਟ ਸੇਵਾ"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਮਿਟਾਇਆ ਜਾਏਗਾ"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਵਰਤੀ ਨਹੀਂ ਜਾ ਸਕਦੀ। ਹੁਣ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦਾ ਡਾਟਾ ਮਿਟਾਇਆ ਜਾਵੇਗਾ।\n\nਜੇਕਰ ਤੁਹਾਡੇ ਕੋਲ ਕੋਈ ਸਵਾਲ ਹਨ, ਤਾਂ ਆਪਣੀ ਸੰਸਥਾ ਦੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ਵੱਲੋਂ ਪ੍ਰਿੰਟ ਕਰਨਾ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
@@ -2206,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"ਹੁਣ ਤੁਸੀਂ ਕੁਝ ਜਾਂ ਪੂਰੀ ਸਕ੍ਰੀਨ ਵੱਡਦਰਸ਼ੀ ਕਰ ਸਕਦੇ ਹੋ"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਚਾਲੂ ਕਰੋ"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ਖਾਰਜ ਕਰੋ"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"ਜਾਰੀ ਰੱਖਣ ਲਈ, <b><xliff:g id="APP">%s</xliff:g></b> ਨੂੰ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਤੱਕ ਪਹੁੰਚ ਦੀ ਲੋੜ ਹੈ।"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ਜਾਰੀ ਰੱਖਣ ਲਈ, <b><xliff:g id="APP">%s</xliff:g></b> ਨੂੰ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰਾ ਤੱਕ ਪਹੁੰਚ ਦੀ ਲੋੜ ਹੈ।"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ਚਾਲੂ ਕਰੋ"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ਸੈਂਸਰ ਪਰਦੇਦਾਰੀ"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 3711cb7..f73b382 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -150,7 +150,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Tylko Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Połączenia przez różne karty SIM z <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Zapasowa metoda wykonywania połączeń <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nieprzekierowane"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundach"</string>
@@ -207,8 +207,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Usługa powiadomień czujnika"</string>
<string name="twilight_service" msgid="8964898045693187224">"Usługa Zmierzch"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Wykrywanie strefy czasowej (brak połączenia)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Usługa synchronizacji czasu na podstawie sygnału GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Twoje urządzenie zostanie wyczyszczone"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Nie można użyć aplikacji administratora. Dane z urządzenia zostaną wykasowane.\n\nJeśli masz pytania, skontaktuj się z administratorem organizacji."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Drukowanie wyłączone przez: <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2273,12 +2272,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Możesz teraz powiększyć część lub całość ekranu"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Włącz w Ustawieniach"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Odrzuć"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Aby kontynuować, musisz przyznać aplikacji „<xliff:g id="APP">%s</xliff:g>” dostęp do mikrofonu urządzenia."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Aby kontynuować, musisz przyznać aplikacji „<xliff:g id="APP">%s</xliff:g>” dostęp do aparatu urządzenia."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Włącz"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Poufność danych z czujników"</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 89b57ef..693af83 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Somente Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Chamadas entre chips da <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Chamadas alternativas da <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não encaminhado"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -203,7 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Serviço de notificações do sensor"</string>
<string name="twilight_service" msgid="8964898045693187224">"Serviço de crepúsculo"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detector de fuso horário (sem conectividade)"</string>
- <string name="gnss_time_update_service" msgid="9039489496037616095">"Serviço de atualização de horário do GNSS"</string>
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Serviço de atualização de horário do Sistema Global de Navegação por Satélites (GNSS, na sigla em inglês)"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Seu dispositivo será limpo"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Não é possível usar o aplicativo para administrador. Seu dispositivo passará por uma limpeza agora.\n\nEm caso de dúvidas, entre em contato com o administrador da sua organização."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Impressão desativada por <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 4fe13f8..e00034a 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Apenas Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Chamadas com vários cartões SIM"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Fazer uma cópia de segurança das chamadas <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não reencaminhado"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -2068,7 +2068,7 @@
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string>
<string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Conversa"</string>
- <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Conversa de grupo"</string>
+ <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Conversa em grupo"</string>
<string name="unread_convo_overflow" msgid="920517615597353833">"> <xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>"</string>
<string name="resolver_personal_tab" msgid="2051260504014442073">"Pessoal"</string>
<string name="resolver_work_tab" msgid="2690019516263167035">"Trabalho"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 89b57ef..693af83 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Somente Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Chamadas entre chips da <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Chamadas alternativas da <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não encaminhado"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -203,7 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Serviço de notificações do sensor"</string>
<string name="twilight_service" msgid="8964898045693187224">"Serviço de crepúsculo"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detector de fuso horário (sem conectividade)"</string>
- <string name="gnss_time_update_service" msgid="9039489496037616095">"Serviço de atualização de horário do GNSS"</string>
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Serviço de atualização de horário do Sistema Global de Navegação por Satélites (GNSS, na sigla em inglês)"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Seu dispositivo será limpo"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Não é possível usar o aplicativo para administrador. Seu dispositivo passará por uma limpeza agora.\n\nEm caso de dúvidas, entre em contato com o administrador da sua organização."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Impressão desativada por <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 38f9854..5fd6f1a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -149,7 +149,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Numai Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Apelare pe mai multe carduri SIM <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Apelare de rezervă prin <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecționată"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> după <xliff:g id="TIME_DELAY">{2}</xliff:g> secunde"</string>
@@ -205,8 +205,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Serviciu pentru notificări de la senzori"</string>
<string name="twilight_service" msgid="8964898045693187224">"Serviciul Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detector de fus orar (fără conexiune)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Serviciul de actualizare a orei bazat pe GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Datele de pe dispozitiv vor fi șterse"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Aplicația de administrare nu poate fi utilizată. Dispozitivul va fi șters.\n\nDacă aveți întrebări, contactați administratorul organizației dvs."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Printare dezactivată de <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2239,12 +2238,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Acum puteți mări o parte sau tot ecranul"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activați din Setări"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Respingeți"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Pentru a continua, <b><xliff:g id="APP">%s</xliff:g></b> necesită acces la microfonul dispozitivului."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Pentru a continua, <b><xliff:g id="APP">%s</xliff:g></b> necesită acces la camera dispozitivului."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activați"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidențialitatea privind senzorii"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 6408cce..d1cd374 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -150,7 +150,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Только Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Перекрестная работа SIM-карт от \"<xliff:g id="SPN">%s</xliff:g>\""</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Резервный способ связи: <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не переадресовано"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> через <xliff:g id="TIME_DELAY">{2}</xliff:g> с."</string>
@@ -207,8 +207,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Сервис для обработки уведомлений от датчиков"</string>
<string name="twilight_service" msgid="8964898045693187224">"Сервис для определения наступления сумерек"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Определитель часового пояса (работает без Интернета)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Синхронизация времени с помощью ГНСС"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Все данные с устройства будут удалены"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Невозможно использовать приложение для администрирования. С устройства будут удалены все данные.\n\nЕсли у вас возникли вопросы, обратитесь к администратору."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Функция печати отключена приложением \"<xliff:g id="OWNER_APP">%s</xliff:g>\""</string>
@@ -2273,12 +2272,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Теперь можно увеличивать весь экран или его часть."</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Включить в настройках"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Закрыть"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Чтобы продолжить, предоставьте приложению <b><xliff:g id="APP">%s</xliff:g></b> доступ к микрофону устройства."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Чтобы продолжить, предоставьте приложению <b><xliff:g id="APP">%s</xliff:g></b> доступ к камере устройства."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Включить"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Конфиденциальность датчиков"</string>
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index a6d54f4..d102183 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi පමණයි"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> හරස් SIM ඇමතුම"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> උපස්ථ ඇමතුම"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ඉදිරියට නොයවන ලදි"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: තත්පර <xliff:g id="TIME_DELAY">{2}</xliff:g> ට පසුව <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"සංවේදක දැනුම් දීමේ සේවාව"</string>
<string name="twilight_service" msgid="8964898045693187224">"ඇඳිරි සේවාව"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"වේලා කලාප අනාවරකය (සම්බන්ධතාවක් නොමැත)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS වේලා යාවත්කාලීන සේවාව"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"ඔබගේ උපාංගය මකා දැමෙනු ඇත"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"පරිපාලක යෙදුම භාවිතා කළ නොහැකිය. ඔබේ උපාංගය දැන් මකා දමනු ඇත.\n\nඔබට ප්රශ්න තිබේ නම්, ඔබේ සංවිධානයේ පරිපාලකට අමතන්න."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> විසින් මුද්රණය කිරීම අබල කර ඇත."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"ඔබට දැන් තිරයේ සමහර හෝ සියලු දේ විශාලනය කළ හැකිය"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"සැකසීම් තුළ ක්රියාත්මක කරන්න"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ඉවත ලන්න"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"දිගටම කර ගෙන යාමට, <b><xliff:g id="APP">%s</xliff:g></b> හට ඔබගේ උපාංගයෙහි මයික්රෆෝනයට ප්රවේශය අවශ්යයි."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"දිගටම කර ගෙන යාමට, <b><xliff:g id="APP">%s</xliff:g></b> හට ඔබගේ උපාංගයෙහි කැමරාවට ප්රවේශය අවශ්යයි."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ක්රියාත්මක කරන්න"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"සංවේදක පෞද්ගලිකත්වය"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index a478083..ffa4b26 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -150,7 +150,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Len Wi‑Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Volanie naprieč SIM kartami"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> – záložné volanie"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nepresmerované"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> s"</string>
@@ -207,8 +207,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Služba upozornení senzora"</string>
<string name="twilight_service" msgid="8964898045693187224">"Služba stmievania"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detektor časového pásma (bez pripojenia)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Služba na aktualizáciu času globálneho družicového polohového systému"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Vaše zariadenie bude vymazané"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Daná aplikácia na správu sa nedá použiť. Vaše zariadenie bude vymazané.\n\nV prípade otázok kontaktujte správcu organizácie."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Tlač zakázala aplikácia <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2273,12 +2272,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Teraz môžete zväčšiť celú obrazovku alebo jej časť"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Zapnúť v Nastaveniach"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Zavrieť"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Ak chcete pokračovať, <b><xliff:g id="APP">%s</xliff:g></b> požaduje prístup k mikrofónu zariadenia."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Ak chcete pokračovať, <b><xliff:g id="APP">%s</xliff:g></b> požaduje prístup k fotoaparátu zariadenia."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Zapnúť"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ochrana súkromia senzorov"</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index b353f45..d09ef04 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -150,7 +150,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Klicanje ne glede na kartico SIM operaterja <xliff:g id="SPN">%s</xliff:g>"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ni posredovano"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po toliko sekundah: <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -207,8 +208,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Storitev obvestil tipal"</string>
<string name="twilight_service" msgid="8964898045693187224">"Storitev Somrak"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Zaznavanje časovnega pasu (brez povezave)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Storitev posodobitve ure po sistemu GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Podatki v napravi bodo izbrisani"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Skrbniške aplikacije ni mogoče uporabljati. Podatki v napravi bodo izbrisani.\n\nČe imate vprašanja, se obrnite na skrbnika organizacije."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Tiskanje je onemogočil pravilnik <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2273,12 +2273,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Zdaj lahko povečate del zaslona ali celotni zaslon"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Vklopite v nastavitvah"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Opusti"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Za nadaljevanje potrebuje aplikacija <b><xliff:g id="APP">%s</xliff:g></b> dostop do mikrofona v napravi."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Za nadaljevanje potrebuje aplikacija <b><xliff:g id="APP">%s</xliff:g></b> dostop do fotoaparata v napravi."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Vklopi"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Zasebnost pri uporabi tipal"</string>
</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 86f191f..592e54d 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Vetëm Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Telefonatat e kryqëzuara të kartës SIM nga <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Opsioni rezervë i telefonatave të <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nuk u transferua"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pas <xliff:g id="TIME_DELAY">{2}</xliff:g> sekondash"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Shërbimi i njoftimeve të sensorit"</string>
<string name="twilight_service" msgid="8964898045693187224">"Shërbimi i muzgut"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Zbuluesi i brezit orar (nuk nevojitet lidhja)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Shërbimi i përditësimit të kohës GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Pajisja do të spastrohet"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Aplikacioni i administratorit nuk mund të përdoret. Pajisja jote tani do të fshihet.\n\nNëse ke pyetje, kontakto me administratorin e organizatës."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Printimi është çaktivizuar nga <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Tani mund t\'i zmadhosh një pjesë apo të gjithë ekranin tënd"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Aktivizo te \"Cilësimet\""</string>
<string name="dismiss_action" msgid="1728820550388704784">"Hiq"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Për të vazhduar, <b><xliff:g id="APP">%s</xliff:g></b> ka nevojë të qaset në mikrofonin e pajisjes sate."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Për të vazhduar, <b><xliff:g id="APP">%s</xliff:g></b> ka nevojë të qaset në kamerën e pajisjes sate."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktivizo"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatësia e sensorit"</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 594a2ee..65037a0 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -149,7 +149,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само WiFi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Позиви са више SIM картица за оператера <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> резервни начин за позивање"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> након <xliff:g id="TIME_DELAY">{2}</xliff:g> секунде/и"</string>
@@ -205,8 +205,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Услуга обавештења сензора"</string>
<string name="twilight_service" msgid="8964898045693187224">"Услуга Сумрак"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Детектор временске зоне (нема интернет везе)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS услуга за ажурирање времена"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Уређај ће бити обрисан"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Не можете да користите ову апликацију за администраторе. Уређај ће сада бити обрисан.\n\nАко имате питања, контактирајте администратора организације."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Штампање је онемогућила апликација <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2239,12 +2238,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Можете да увећате део екрана или цео екран"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Укључите у Подешавањима"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Одбаци"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"<b><xliff:g id="APP">%s</xliff:g></b> захтева приступ микрофону уређаја ради настављања."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> захтева приступ камери уређаја ради настављања."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Укључи"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Приватност сензора"</string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index bf498ab..ef7a102 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Endast Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – samtal mellan SIM-kort"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> används som reserv för samtal"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Vidarebefordras inte"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Sensor Notification Service"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight Service"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Tidszondetektering (ingen anslutning)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Tjänst för uppdatering av GNSS-tid"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Enheten kommer att rensas"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Det går inte att använda administratörsappen. Enheten rensas.\n\nKontakta organisationens administratör om du har några frågor."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Utskrift har inaktiverats av <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Nu kan du förstora delar av eller hela skärmen"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Aktivera i inställningarna"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Stäng"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"<b><xliff:g id="APP">%s</xliff:g></b> behöver behörighet till enhetens mikrofon för att fortsätta."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> behöver behörighet till enhetens kamera för att fortsätta."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktivera"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorintegritet"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 93f0ed9..cc82f4e 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi pekee"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Kipengele cha Kupiga Simu Kupitia SIM Tofauti"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>Kupiga Simu Kupitia Mtandao Mbadala"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Haijatumiwa mwingine"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> baada ya sekunde <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Huduma ya Arifa ya Kitambuzi"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight Service"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Kitambua Saa za Eneo (Hakuna muunganisho)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Huduma ya Kusasisha Saa za GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Data iliyomo kwenye kifaa chako itafutwa"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Huwezi kutumia programu ya msimamizi. Sasa data iliyo kwenye kifaa chako itafutwa.\n\nIkiwa una maswali yoyote, wasiliana na msimamizi wa shirika lako."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Kipengele cha kuchapisha kimezimwa na <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Sasa unaweza kukuza sehemu ya au skrini yako yote"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Washa katika Mipangilio"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Ondoa"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Ili uendelee, <b><xliff:g id="APP">%s</xliff:g></b> inahitaji ruhusa ya kufikia maikrofoni ya kifaa chako."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Ili uendelee, <b><xliff:g id="APP">%s</xliff:g></b> inahitaji ruhusa ya kufikia kamera ya kifaa chako."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Washa"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Faragha ya Kitambuzi"</string>
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 527c50f..ca90171 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"வைஃபை மட்டும்"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> கிராஸ்-சிம் அழைப்பு"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> காப்புப் பிரதி அழைப்பு"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: பகிரப்படவில்லை"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> வினாடிகளுக்குப் பிறகு <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ஐப் பகிர்"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"சென்சார் அறிவிப்புச் சேவை"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight சேவை"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"நேர மண்டல டிடெக்டர் (இணைப்பு இல்லை)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS நேரப் புதுப்பிப்புச் சேவை"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"சாதனத் தரவு அழிக்கப்படும்"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"நிர்வாகி ஆப்ஸை உபயோகிக்க முடியாது. இப்போது, உங்கள் சாதனம் ஆரம்ப நிலைக்கு மீட்டமைக்கப்படும்.\n\nஏதேனும் கேள்விகள் இருப்பின், உங்கள் நிறுவனத்தின் நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"பிரிண்ட் செய்வதை <xliff:g id="OWNER_APP">%s</xliff:g> தடுத்துள்ளது."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"திரை முழுவதையுமோ ஒரு பகுதியையோ பெரிதாக்கலாம்"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"அமைப்புகளில் ஆன் செய்க"</string>
<string name="dismiss_action" msgid="1728820550388704784">"மூடுக"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"தொடர, உங்கள் சாதனத்தின் மைக்ரோஃபோனை அணுகுவதற்கு <b><xliff:g id="APP">%s</xliff:g></b> ஆப்ஸுக்கு அனுமதி வேண்டும்."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"தொடர, உங்கள் சாதனத்தின் கேமராவை அணுகுவதற்கு <b><xliff:g id="APP">%s</xliff:g></b> ஆப்ஸுக்கு அனுமதி வேண்டும்."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ஆன் செய்"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"சென்சார் தனியுரிமை"</string>
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 2de91ba..b0726e8 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi మాత్రమే"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> క్రాస్-SIM కాలింగ్"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ఫార్వార్డ్ చేయబడలేదు"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> సెకన్ల తర్వాత <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"సెన్సార్ నోటిఫికేషన్ సర్వీస్"</string>
<string name="twilight_service" msgid="8964898045693187224">"ట్విలైట్ సర్వీస్"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"టైమ్ జోన్ డిటెక్టర్ (కనెక్టివిటీ లేదు)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS సమయ అప్డేట్ సర్వీస్"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"మీ పరికరంలోని డేటా తొలగించబడుతుంది"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"నిర్వాహక యాప్ ఉపయోగించడం సాధ్యపడదు. మీ పరికరంలోని డేటా ఇప్పుడు తొలగించబడుతుంది.\n\nమీకు ప్రశ్నలు ఉంటే, మీ సంస్థ యొక్క నిర్వాహకులను సంప్రదించండి."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"ముద్రణ <xliff:g id="OWNER_APP">%s</xliff:g> ద్వారా నిలిపివేయబడింది."</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"స్క్రీన్ మొత్తం లేదా కొంత భాగాన్ని జూమ్ చేయవచ్చు"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"సెట్టింగ్లలో ఆన్ చేయండి"</string>
<string name="dismiss_action" msgid="1728820550388704784">"విస్మరించు"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"కొనసాగించడానికి, <b><xliff:g id="APP">%s</xliff:g></b>కు మీ పరికరం యొక్క మైక్రోఫోన్ యాక్సెస్ అవసరం."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"కొనసాగించడానికి, <b><xliff:g id="APP">%s</xliff:g></b&gtకు మీ పరికరం యొక్క కెమెరా యాక్సెస్ అవసరం."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ఆన్ చేయి"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"సెన్సార్ గోప్యత"</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index aa3331a..5079f23 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi เท่านั้น"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"การโทรข้ามซิม <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"การสำรองข้อมูลการโทรจาก <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ไม่ได้โอนสาย"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> หลังผ่านไป <xliff:g id="TIME_DELAY">{2}</xliff:g> วินาที"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"บริการแจ้งเตือนเกี่ยวกับเซ็นเซอร์"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight Service"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"ตัวตรวจจับเขตเวลา (ไม่มีการเชื่อมต่อ)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"บริการอัปเดตเวลาของ GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"ระบบจะลบข้อมูลในอุปกรณ์ของคุณ"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"ใช้แอปผู้ดูแลระบบนี้ไม่ได้ ขณะนี้ระบบจะลบข้อมูลในอุปกรณ์ของคุณ\n\nโปรดติดต่อผู้ดูแลระบบขององค์กรหากมีคำถาม"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ปิดใช้การพิมพ์แล้ว"</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"คุณสามารถขยายหน้าจอบางส่วนหรือทั้งหมดได้แล้วตอนนี้"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"เปิดในการตั้งค่า"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ปิด"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"<b><xliff:g id="APP">%s</xliff:g></b> ต้องได้รับสิทธิ์เข้าถึงไมโครโฟนของอุปกรณ์เพื่อดำเนินการต่อ"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> ต้องได้รับสิทธิ์เข้าถึงกล้องของอุปกรณ์เพื่อดำเนินการต่อ"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"เปิด"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ความเป็นส่วนตัวสำหรับเซ็นเซอร์"</string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index d3a1fe7..81bfe52 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi lang"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"Cross-SIM na Pagtawag sa <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Backup na Pagtawag"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Hindi naipasa"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pagkatapos ng <xliff:g id="TIME_DELAY">{2}</xliff:g> (na) segundo"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Serbisyo ng Notification ng Sensor"</string>
<string name="twilight_service" msgid="8964898045693187224">"Serbisyo ng Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Detector ng Time Zone (Walang koneksyon)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Serbisyo sa Pag-update ng Oras ng GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Buburahin ang iyong device"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Hindi magamit ang admin app. Mabubura na ang iyong device.\n\nKung mayroon kang mga tanong, makipag-ugnayan sa admin ng iyong organisasyon."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Na-disable ng <xliff:g id="OWNER_APP">%s</xliff:g> ang pag-print."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Mama-magnify na ang bahagi o kabuuan ng screen mo"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"I-on sa Mga Setting"</string>
<string name="dismiss_action" msgid="1728820550388704784">"I-dismiss"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Para magpatuloy, kailangan ng <b><xliff:g id="APP">%s</xliff:g></b> ng access sa mikropono ng iyong device."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para magpatuloy, kailangan ng <b><xliff:g id="APP">%s</xliff:g></b> ng access sa camera ng iyong device."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"I-on"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacy ng Sensor"</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 26108fb..7095bb8 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Yalnızca kablosuz"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Çapraz SIM Araması"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Yedek Arama"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yönlendirilmedi"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> saniye sonra <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Sensör Bildirim Hizmeti"</string>
<string name="twilight_service" msgid="8964898045693187224">"Alacakaranlık Hizmeti"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Zaman Dilimi Algılayıcı (Bağlantı yok)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS Zaman Güncelleme Hizmeti"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Cihazınız silinecek"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Yönetim uygulaması kullanılamıyor. Cihazınız şimdi silinecek.\n\nSorularınız varsa kuruluşunuzun yöneticisine başvurun."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Yazdırma işlemi <xliff:g id="OWNER_APP">%s</xliff:g> tarafından devre dışı bırakıldı."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Artık ekranınızın bir kısmını veya tamamını büyütebilirsiniz"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ayarlar\'da aç"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Kapat"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Devam etmek için <b><xliff:g id="APP">%s</xliff:g></b> uygulamasının cihazınızın mikrofonuna erişmesi gerekiyor."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Devam etmek için <b><xliff:g id="APP">%s</xliff:g></b> uygulamasının cihazınızın kamerasına erişmesi gerekiyor."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aç"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensör Gizliliği"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index abb1b7d..9864b4b 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -150,7 +150,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Лише Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> – виклики між SIM-картами"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>: резервний спосіб здійснення викликів"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не переслано"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> після <xliff:g id="TIME_DELAY">{2}</xliff:g> сек."</string>
@@ -207,8 +207,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Сервіс \"Сповіщення датчика\""</string>
<string name="twilight_service" msgid="8964898045693187224">"Сервіс \"Сутінки\""</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Визначення часового поясу (без Інтернету)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Сервіс оновлення часу GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"З вашого пристрою буде стерто всі дані"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Не можна запускати додаток для адміністраторів. Буде відновлено заводські налаштування пристрою.\n\nЯкщо у вас є запитання, зв’яжіться з адміністратором своєї організації."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Додаток <xliff:g id="OWNER_APP">%s</xliff:g> вимкнув друк."</string>
@@ -2273,12 +2272,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Тепер можна збільшити весь екран або його частину"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Увімкнути в налаштуваннях"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Закрити"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Щоб продовжити, надайте додатку <b><xliff:g id="APP">%s</xliff:g></b> доступ до мікрофона пристрою."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Щоб продовжити, надайте додатку <b><xliff:g id="APP">%s</xliff:g></b> доступ до камери пристрою."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Увімкнути"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Конфіденційність датчиків"</string>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index f247889..64fb2fc 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -148,7 +148,8 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"صرف Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> کراس sim کالنگ"</string>
+ <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
+ <skip />
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : فارورڈ نہیں کی گئی"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد از <xliff:g id="TIME_DELAY">{2}</xliff:g> سیکنڈ"</string>
@@ -203,8 +204,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"سینسر نوٹیفکیشن سروس"</string>
<string name="twilight_service" msgid="8964898045693187224">"شفقی سروس"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"ٹائم زون ڈیٹیکٹر (کوئی کنیکٹوٹی نہیں ہے)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS کی ٹائم اپ ڈیٹ سروس"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"آپ کا آلہ صاف کر دیا جائے گا"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"منتظم کی ایپ استعمال نہیں کی جا سکتی۔ آپ کا آلہ اب مٹا دیا جائے گا۔\n\nاگر آپ کے سوالات ہیں تو اپنی تنظیم کے منتظم سے رابطہ کریں۔"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> نے پرنٹنگ کو غیر فعال کر دیا ہے۔"</string>
@@ -2205,12 +2205,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"اب آپ اپنی تمام یا کچھ اسکرین کو بڑا کر سکتے ہیں"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ترتیبات میں آن کریں"</string>
<string name="dismiss_action" msgid="1728820550388704784">"برخاست کریں"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"جاری رکھنے کیلئے <xliff:g id="APP">%s</xliff:g><b><b> کو آپ کے آلے کے مائیکروفون تک رسائی درکار ہے۔"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"جاری رکھنے کیلئے <b><xliff:g id="APP">%s</xliff:g></b> کو آپ کے آلے کے کیمرے تک رسائی درکار ہے۔"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"آن کریں"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"سینسر کی رازداری"</string>
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index e72114a..6612cc9 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -57,7 +57,7 @@
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"Kiruvchi raqami"</string>
- <string name="ClirMmi" msgid="6752346475055446417">"Chaqiruvchi raqamini yashirish"</string>
+ <string name="ClirMmi" msgid="6752346475055446417">"Abonent raqamini berkitish"</string>
<string name="ColpMmi" msgid="4736462893284419302">"Qo‘ng‘iroq qiluvchining raqami"</string>
<string name="ColrMmi" msgid="5889782479745764278">"Qo‘ng‘iroq qiluvchining raqamini cheklash"</string>
<string name="CfMmi" msgid="8390012691099787178">"Chaqiruvlarni uzatish"</string>
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Faqat Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Kross-SIM chaqiruvlar"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Zaxiraviy chaqiruv"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yo‘naltirilmadi"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> soniyadan so‘ng"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Sensorli bildirishnoma xizmati"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight xizmati"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Vaqt mintaqasini aniqlagich (Oflayn)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS yordamida vaqtni yangilash xizmati"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Qurilmangizdagi ma’lumotlar o‘chirib tashlanadi"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Administrator ilovasini ishlatib bo‘lmaydi. Qurilmada barcha ma’lumotlar o‘chirib tashlanadi.\n\nSavollaringiz bo‘lsa, administrator bilan bog‘laning."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Chop etish funksiyasi <xliff:g id="OWNER_APP">%s</xliff:g> tomonidan faolsizlantirilgan."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Ekranni toʻliq yoki qisman kattalashtirish mumkin"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Sozlamalar orqali yoqish"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Yopish"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Davom etish uchun <b><xliff:g id="APP">%s</xliff:g></b> mikrofoningizdan foydalanishi kerak."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Davom etish uchun <b><xliff:g id="APP">%s</xliff:g></b> qurilmangiz kamerasiga kirishi kerak."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Yoqish"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorlar maxfiyligi"</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 55f5f19..8f8e0bc 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Chỉ Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> Gọi bằng nhiều SIM"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Gọi điện dự phòng <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Không được chuyển tiếp"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> sau <xliff:g id="TIME_DELAY">{2}</xliff:g> giây"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Dịch vụ Thông báo của cảm biến"</string>
<string name="twilight_service" msgid="8964898045693187224">"Dịch vụ Twilight"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Trình phát hiện múi giờ (Không có kết nối)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Dịch vụ cập nhật thời gian GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Thiết bị của bạn sẽ bị xóa"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Không thể sử dụng ứng dụng quản trị. Thiết bị của bạn sẽ bị xóa ngay bây giờ.\n\nHãy liên hệ với quản trị viên của tổ chức nếu bạn có thắc mắc."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> đã tắt tính năng in."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Giờ đây, bạn có thể phóng to một phần hoặc toàn màn hình"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Bật trong phần Cài đặt"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Đóng"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Để tiếp tục, <b><xliff:g id="APP">%s</xliff:g></b> cần quyền truy cập vào micrô trên thiết bị của bạn."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Để tiếp tục, <b><xliff:g id="APP">%s</xliff:g></b> cần quyền truy cập vào máy ảnh trên thiết bị của bạn."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Bật"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Quyền riêng tư khi sử dụng cảm biến"</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 347ad56..af5f846 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"仅限 WLAN"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> 跨 SIM 卡通话"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>备用通话"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g>秒后<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"传感器通知服务"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight 服务"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"时区检测器(无网络连接)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS 时间更新服务"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"系统将清空您的设备"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"无法使用管理应用,系统现在将清空您的设备。\n\n如有疑问,请与您所在单位的管理员联系。"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"“<xliff:g id="OWNER_APP">%s</xliff:g>”已停用打印功能。"</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"您现在可以放大屏幕上的部分或所有内容"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"在“设置”中开启"</string>
<string name="dismiss_action" msgid="1728820550388704784">"关闭"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"如要继续操作,请向<b><xliff:g id="APP">%s</xliff:g></b>授予设备的麦克风使用权。"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"如要继续操作,请向<b><xliff:g id="APP">%s</xliff:g></b>授予设备的相机使用权。"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"开启"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"传感器隐私权"</string>
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 79ce664..65586f7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"只限 Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g> 跨 SIM 卡通話"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"「<xliff:g id="SPN">%s</xliff:g>」備用通話網路"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> 於 <xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後轉接"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"感應器通知服務"</string>
<string name="twilight_service" msgid="8964898045693187224">"暮光服務"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"時區偵測器 (沒有連線)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS 時間更新服務"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"您的裝置將被清除"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"無法使用管理員應用程式。系統會現在清除您的裝置。\n\n如有任何疑問,請聯絡您的機構管理員。"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"「<xliff:g id="OWNER_APP">%s</xliff:g>」暫停了列印。"</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"您現在可以放大部分或整個畫面"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"在「設定」中開啟"</string>
<string name="dismiss_action" msgid="1728820550388704784">"關閉"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"如要繼續,<b><xliff:g id="APP">%s</xliff:g></b> 需要裝置的麥克風存取權。"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"如要繼續,<b><xliff:g id="APP">%s</xliff:g></b> 需要裝置的相機存取權。"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"開啟"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"感應器私隱"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 15a1201..6facceb 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"只限 Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"「<xliff:g id="SPN">%s</xliff:g>」跨 SIM 卡通話"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"「<xliff:g id="SPN">%s</xliff:g>」備用通話網路"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:未轉接"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後 <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"感應器通知服務"</string>
<string name="twilight_service" msgid="8964898045693187224">"Twilight 服務"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"時區偵測器 (不必連上網路)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS 時間更新服務"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"你的裝置資料將遭到清除"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"無法使用管理應用程式,系統現在將清除你裝置中的資料。\n\n如有任何問題,請與貴機構的管理員聯絡。"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"「<xliff:g id="OWNER_APP">%s</xliff:g>」已停用列印功能。"</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"你可以放大局部或整個畫面"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"在「設定」中開啟"</string>
<string name="dismiss_action" msgid="1728820550388704784">"關閉"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"如要繼續操作,請將裝置的麥克風存取權授予「<xliff:g id="APP">%s</xliff:g>」<b></b>。"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"如要繼續操作,請將裝置的相機存取權授予「<xliff:g id="APP">%s</xliff:g>」<b></b>。"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"開啟"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"感應器隱私權"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index b18afe7..3b22e94 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"I-Wi-Fi kuphela"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="779976494687695991">"<xliff:g id="SPN">%s</xliff:g>Ukwenza ikholi kwe-Cross Sim"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Ukushaya Ikholi Kwekhopi Yasenqolobaneni"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Akudlulisiwe"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> emuva kwamasekhondi angu-<xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -203,8 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"Isevisi Yesaziso Senzwa"</string>
<string name="twilight_service" msgid="8964898045693187224">"Isevisi Yangovivi"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Isitholi Sezoni Yesikhathi (Akukho ukuxhumana)"</string>
- <!-- no translation found for gnss_time_update_service (9039489496037616095) -->
- <skip />
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"Isevisi Ebuyekeziwe Yesikhathi se-GNSS"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"Idivayisi yakho izosulwa"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Uhlelo lokusebenza lomlawuli alikwazi ukusetshenziswa. Idivayisi yakho manje izosuswa.\n\nUma unemibuzo, xhumana nomlawuli wezinhlangano zakho."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Ukuphrinta kukhutshazwe nge-<xliff:g id="OWNER_APP">%s</xliff:g>."</string>
@@ -2205,12 +2204,8 @@
<string name="window_magnification_prompt_content" msgid="4166711383253283838">"Manje usungakwazi ukukhulisa esinye noma sonke isikrini sakho"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Vula Kumasethingi"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Cashisa"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content (8063355861118105607) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content (4738005643315863736) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7921147002346108119) -->
- <skip />
- <!-- no translation found for sensor_privacy_notification_channel_label (936036783155261349) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"Ukuze uqhubeke, <b>i-<xliff:g id="APP">%s</xliff:g></b> idinga ukufinyelela imakrofoni yedivayisi yakho."</string>
+ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Ukuze uqhubeke, <b>i-<xliff:g id="APP">%s</xliff:g></b> idinga ukufinyelela ikhamera yakho."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Vula"</string>
+ <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ubumfihlo Benzwa"</string>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 14f1e0e..eb86709 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -87,14 +87,12 @@
theme does not set this value, meaning it is based on whether the
window is floating. -->
<attr name="backgroundDimEnabled" format="boolean" />
- <!-- When windowBackgroundBlurEnabled is set, this is the amount of blur to apply
- behind the window. The range is from 0, which means no blur, to 150.
- @hide @SystemApi -->
- <attr name="windowBackgroundBlurRadius" format="dimension"/>
- <!-- If set, the area behind the window will be blurred with radius
- windowBackgroundBlurRadius.
- @hide @SystemApi -->
- <attr name="windowBackgroundBlurEnabled" format="boolean" />
+ <!-- When windowBlurBehindEnabled is set, this is the amount of blur to apply
+ behind the window. The range is from 0, which means no blur, to 150. -->
+ <attr name="windowBlurBehindRadius" format="dimension"/>
+ <!-- If set, everything behind the window will be blurred with radius
+ windowBackgroundBlurRadius. -->
+ <attr name="windowBlurBehindEnabled" format="boolean" />
<!-- Color of background imagery used for popup windows. -->
@@ -1974,8 +1972,8 @@
<attr name="textColor" />
<attr name="backgroundDimEnabled" />
<attr name="backgroundDimAmount" />
- <attr name="windowBackgroundBlurEnabled" />
- <attr name="windowBackgroundBlurRadius" />
+ <attr name="windowBlurBehindEnabled" />
+ <attr name="windowBlurBehindRadius" />
<attr name="windowActionBar" />
<attr name="windowActionModeOverlay" />
<attr name="windowActionBarOverlay" />
@@ -4069,6 +4067,13 @@
<attr name="dial" format="reference"/>
<attr name="hand_hour" format="reference"/>
<attr name="hand_minute" format="reference"/>
+ <attr name="hand_second" format="reference"/>
+ <!-- Specifies the time zone to use. When this attribute is specified, the
+ TextClock will ignore the time zone of the system. To use the user's
+ time zone, do not specify this attribute. The default value is the
+ user's time zone. Please refer to {@link java.util.TimeZone} for more
+ information about time zone ids. -->
+ <attr name="timeZone" format="string"/>
</declare-styleable>
<declare-styleable name="Button">
</declare-styleable>
@@ -8435,6 +8440,9 @@
<attr name="errorColor" format="color|reference" />
<!-- The success color -->
<attr name="successColor" format="color|reference"/>
+ <!-- The dot color -->
+ <attr name="dotColor" format="color|reference"/>
+
</declare-styleable>
<!-- =============================== -->
@@ -9193,6 +9201,9 @@
<!-- @hide From Theme.colorBackground, used for the TaskDescription background
color. -->
<attr name="colorBackground" />
+ <!-- @hide From Theme.colorBackgroundFloating, used for the TaskDescription background
+ color floating. -->
+ <attr name="colorBackgroundFloating" />
<!-- @hide From Theme.statusBarColor, used for the TaskDescription status bar color. -->
<attr name="statusBarColor"/>
<!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 6e9eb82..7f3e177 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -17,9 +17,9 @@
<!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
overlaying new theme colors. -->
<resources>
- <color name="primary_device_default_dark">@color/system_main_900</color>
+ <color name="primary_device_default_dark">@color/system_main_800</color>
<color name="primary_device_default_light">@color/system_main_50</color>
- <color name="primary_device_default_settings">@color/system_main_900</color>
+ <color name="primary_device_default_settings">@color/system_main_800</color>
<color name="primary_device_default_settings_light">@color/primary_device_default_light</color>
<color name="primary_dark_device_default_dark">@color/primary_device_default_dark</color>
<color name="primary_dark_device_default_light">@color/primary_device_default_light</color>
@@ -37,17 +37,17 @@
<color name="accent_device_default_dark">@color/system_accent_200</color>
<color name="accent_device_default">@color/accent_device_default_light</color>
- <color name="background_device_default_dark">@color/system_main_900</color>
+ <color name="background_device_default_dark">@color/system_main_800</color>
<color name="background_device_default_light">@color/system_main_50</color>
- <color name="background_floating_device_default_dark">@color/system_main_800</color>
+ <color name="background_floating_device_default_dark">@color/system_main_900</color>
<color name="background_floating_device_default_light">@color/system_main_100</color>
<!-- Error color -->
<color name="error_color_device_default_dark">@color/error_color_material_dark</color>
<color name="error_color_device_default_light">@color/error_color_material_light</color>
- <color name="list_divider_color_light">#ffdadce0</color>
- <color name="list_divider_color_dark">#85ffffff</color>
+ <color name="list_divider_color_light">@color/system_main_500</color>
+ <color name="list_divider_color_dark">@color/system_main_400</color>
<color name="list_divider_opacity_device_default_light">@android:color/white</color>
<color name="list_divider_opacity_device_default_dark">@android:color/white</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5e0cda6..3790aa4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -517,6 +517,9 @@
<!-- Flag indicating whether we should enable smart battery. -->
<bool name="config_smart_battery_available">false</bool>
+ <!-- Flag indicating whether we should enable camera-based autorotate -->
+ <bool name="config_camera_autorotate">false</bool>
+
<!-- Fast brightness animation ramp rate in brightness units per second-->
<integer translatable="false" name="config_brightness_ramp_rate_fast">180</integer>
@@ -660,9 +663,15 @@
The default is false. -->
<bool name="config_lidControlsSleep">false</bool>
- <!-- The device state (supplied by DeviceStateManager) that should be treated as folded by the
- display fold controller. Default is DeviceStateManager.INVALID_DEVICE_STATE. -->
- <integer name="config_foldedDeviceState">-1</integer>
+ <!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
+ display fold controller. Default is empty. -->
+ <integer-array name="config_foldedDeviceStates">
+ <!-- Example:
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ -->
+ </integer-array>
<!-- Indicate the display area rect for foldable devices in folded state. -->
<string name="config_foldedArea"></string>
@@ -822,28 +831,21 @@
<!-- B y-intercept --> <item>-0.198650895</item>
</string-array>
- <string-array name="config_reduceBrightColorsCoefficientsNative">
- <!-- R a-coefficient --> <item>-0.691218457</item>
- <!-- R b-coefficient --> <item>0.050135153</item>
- <!-- R y-intercept --> <item>0.917684143</item>
- <!-- G a-coefficient --> <item>-0.691218457</item>
- <!-- G b-coefficient --> <item>0.050135153</item>
- <!-- G y-intercept --> <item>0.917684143</item>
- <!-- B a-coefficient --> <item>-0.691218457</item>
- <!-- B b-coefficient --> <item>0.050135153</item>
- <!-- B y-intercept --> <item>0.917684143</item>
+ <!-- Control whether bright color reduction is available. This should only be enabled on devices
+ that have a HWC implementation that can apply the matrix passed to setColorTransform
+ without impacting power, performance, and app compatibility (e.g. protected content). -->
+ <bool name="config_reduceBrightColorsAvailable">@bool/config_setColorTransformAccelerated</bool>
+
+ <string-array name="config_reduceBrightColorsCoefficientsNonlinear">
+ <!-- a-coefficient --> <item>-0.4429953456</item>
+ <!-- b-coefficient --> <item>-0.2434077725</item>
+ <!-- y-intercept --> <item>0.9809063061</item>
</string-array>
<string-array name="config_reduceBrightColorsCoefficients">
- <!-- R a-coefficient --> <item>0.00000000000000154</item>
- <!-- R b-coefficient --> <item>-1.0</item>
- <!-- R y-intercept --> <item>1.045977011</item>
- <!-- G a-coefficient --> <item>0.00000000000000224</item>
- <!-- G b-coefficient --> <item>-1.0</item>
- <!-- G y-intercept --> <item>1.045977011</item>
- <!-- B a-coefficient --> <item>0.0000000000000022</item>
- <!-- B b-coefficient --> <item>-1.0</item>
- <!-- B y-intercept --> <item>1.045977011</item>
+ <!-- a-coefficient --> <item>-0.000000000000001</item>
+ <!-- b-coefficient --> <item>-0.955555555555554</item>
+ <!-- y-intercept --> <item>1.000000000000000</item>
</string-array>
<!-- Boolean indicating whether display white balance is supported. -->
@@ -3800,6 +3802,15 @@
-->
<string name="config_defaultSearchUiService" translatable="false"></string>
+ <!-- The package name for the system's smartspace service.
+ This service returns smartspace results.
+
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ If no service with the specified name exists on the device, smartspace will be disabled.
+ Example: "com.android.intelligence/.SmartspaceService"
+-->
+ <string name="config_defaultSmartspaceService" translatable="false"></string>
+
<!-- The package name for the system's speech recognition service.
This service must be trusted, as it can be activated without explicit consent of the user.
Example: "com.android.speech/.RecognitionService"
@@ -4395,6 +4406,15 @@
<!--If true, allows the device to load udfps components on older HIDL implementations -->
<bool name="allow_test_udfps" translatable="false" >false</bool>
+ <!-- The properties of a UDFPS sensor in pixels, in the order listed below: -->
+ <integer-array name="config_udfps_sensor_props" translatable="false" >
+ <!--
+ <item>sensorLocationX</item>
+ <item>sensorLocationY</item>
+ <item>sensorRadius</item>
+ -->
+ </integer-array>
+
<!-- Messages that should not be shown to the user during face auth enrollment. This should be
used to hide messages that may be too chatty or messages that the user can't do much about.
Entries are defined in android.hardware.biometrics.face@1.0 types.hal -->
@@ -4636,6 +4656,23 @@
corners of the activity won't be rounded. -->
<integer name="config_letterboxActivityCornersRadius">0</integer>
+ <!-- Corners appearance of the letterbox background.
+ 0 - Solid background using color specified in R.color.config_letterboxBackgroundColor.
+ 1 - Color specified in R.attr.colorBackground for the letterboxed application.
+ 2 - Color specified in R.attr.colorBackgroundFloating for the letterboxed application.
+ If given value is outside of this range, the option 0 will be assummed. -->
+ <integer name="config_letterboxBackgroundType">0</integer>
+
+ <!-- Color of the letterbox background if one following conditions is true
+ - Option 0 is selected for R.integer.config_letterboxBackgroundType.
+ - Option 1 is selected for R.integer.config_letterboxBackgroundType and
+ R.attr.colorBackground isn't specified for the app.
+ - Option 2 is selected for R.integer.config_letterboxBackgroundType and
+ R.attr.colorBackgroundFloating isn't specified for the app.
+ Defaults to black if not specified.
+ -->
+ <color name="config_letterboxBackgroundColor">#000</color>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
@@ -4670,4 +4707,7 @@
<!-- Whether to select voice/data/sms preference without user confirmation -->
<bool name="config_voice_data_sms_auto_fallback">false</bool>
+
+ <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
+ <bool name="config_enableOneHandedKeyguard">false</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 3ae2131..cb16af5 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -622,9 +622,9 @@
aliasing effects). This is only used on circular displays. -->
<dimen name="circular_display_mask_thickness">1px</dimen>
- <dimen name="lock_pattern_dot_line_width">3dp</dimen>
- <dimen name="lock_pattern_dot_size">12dp</dimen>
- <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
+ <dimen name="lock_pattern_dot_line_width">22dp</dimen>
+ <dimen name="lock_pattern_dot_size">14dp</dimen>
+ <dimen name="lock_pattern_dot_size_activated">30dp</dimen>
<dimen name="text_handle_min_size">40dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9c1c51c..6fd3fb1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3048,17 +3048,15 @@
<public name="allowClickWhenDisabled" />
<public name="windowLayoutAffinity" />
<public name="canPauseRecording" />
- <!-- @hide -->
- <!-- @hide @SystemApi -->
- <public name="windowBackgroundBlurRadius"/>
- <!-- @hide @SystemApi -->
- <public name="windowBackgroundBlurEnabled"/>
+ <public name="windowBlurBehindRadius"/>
+ <public name="windowBlurBehindEnabled"/>
<public name="requireDeviceScreenOn" />
<public name="pathSuffix" />
<public name="sspSuffix" />
<public name="pathAdvancedPattern" />
<public name="sspAdvancedPattern" />
<public name="fontProviderSystemFontFamily" />
+ <public name="hand_second" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dfccdf4..80163b1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1881,6 +1881,7 @@
<java-symbol type="bool" name="config_enableServerNotificationEffectsForAutomotive" />
<java-symbol type="bool" name="config_useAttentionLight" />
<java-symbol type="bool" name="config_adaptive_sleep_available" />
+ <java-symbol type="bool" name="config_camera_autorotate"/>
<java-symbol type="bool" name="config_animateScreenLights" />
<java-symbol type="bool" name="config_automatic_brightness_available" />
<java-symbol type="bool" name="config_smart_battery_available" />
@@ -2540,6 +2541,7 @@
<java-symbol type="string" name="config_biometric_prompt_ui_package" />
<java-symbol type="array" name="config_biometric_sensors" />
<java-symbol type="bool" name="allow_test_udfps" />
+ <java-symbol type="array" name="config_udfps_sensor_props" />
<java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
<java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" />
@@ -3191,8 +3193,9 @@
<java-symbol type="integer" name="config_nightDisplayColorTemperatureMax" />
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" />
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
+ <java-symbol type="bool" name="config_reduceBrightColorsAvailable" />
<java-symbol type="array" name="config_reduceBrightColorsCoefficients" />
- <java-symbol type="array" name="config_reduceBrightColorsCoefficientsNative" />
+ <java-symbol type="array" name="config_reduceBrightColorsCoefficientsNonlinear" />
<java-symbol type="array" name="config_availableColorModes" />
<java-symbol type="array" name="config_mappedColorModes" />
<java-symbol type="string" name="config_vendorColorModesRestoreHint" />
@@ -3498,6 +3501,7 @@
<java-symbol type="string" name="config_defaultAppPredictionService" />
<java-symbol type="string" name="config_defaultContentSuggestionsService" />
<java-symbol type="string" name="config_defaultSearchUiService" />
+ <java-symbol type="string" name="config_defaultSmartspaceService" />
<java-symbol type="string" name="config_defaultMusicRecognitionService" />
<java-symbol type="string" name="config_defaultAttentionService" />
<java-symbol type="string" name="config_defaultRotationResolverService" />
@@ -3728,7 +3732,7 @@
<java-symbol type="string" name="config_customCountryDetector" />
<!-- For Foldables -->
- <java-symbol type="integer" name="config_foldedDeviceState" />
+ <java-symbol type="array" name="config_foldedDeviceStates" />
<java-symbol type="string" name="config_foldedArea" />
<java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
@@ -4133,6 +4137,8 @@
<java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
<java-symbol type="integer" name="config_letterboxActivityCornersRadius" />
+ <java-symbol type="integer" name="config_letterboxBackgroundType" />
+ <java-symbol type="color" name="config_letterboxBackgroundColor" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
@@ -4169,4 +4175,6 @@
<java-symbol type="bool" name="config_telephony5gNonStandalone" />
<java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
+
+ <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
</resources>
diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp
new file mode 100644
index 0000000..e178776
--- /dev/null
+++ b/core/tests/GameManagerTests/Android.bp
@@ -0,0 +1,29 @@
+// 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.
+
+android_test {
+ name: "FrameworksCoreGameManagerTests",
+ // Include all test java files
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "junit",
+ "truth-prebuilt",
+ ],
+ libs: ["android.test.runner"],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/GameManagerTests/AndroidManifest.xml b/core/tests/GameManagerTests/AndroidManifest.xml
new file mode 100644
index 0000000..6c28607
--- /dev/null
+++ b/core/tests/GameManagerTests/AndroidManifest.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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.app.gamemanagertests"
+ android:sharedUserId="android.uid.system" >
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.app.gamemanagertests"
+ android:label="Game Manager Tests"/>
+
+</manifest>
diff --git a/core/tests/GameManagerTests/AndroidTest.xml b/core/tests/GameManagerTests/AndroidTest.xml
new file mode 100644
index 0000000..43729923
--- /dev/null
+++ b/core/tests/GameManagerTests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?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.
+ -->
+
+<configuration description="Runs Game Manager Tests.">
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-suite-tag" value="apct-instrumentation"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksCoreGameManagerTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.app.gamemanagertests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
new file mode 100644
index 0000000..8f50051
--- /dev/null
+++ b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
@@ -0,0 +1,83 @@
+/*
+ * 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 static junit.framework.Assert.assertEquals;
+
+import android.app.GameManager.GameMode;
+import android.util.ArrayMap;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link android.app.GameManager}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class GameManagerTests {
+ private static final String PACKAGE_NAME_0 = "com.android.app0";
+ private static final String PACKAGE_NAME_1 = "com.android.app1";
+
+ private TestGameManagerService mService;
+ private GameManager mGameManager;
+
+ @Before
+ public void setUp() {
+ mService = new TestGameManagerService();
+ mGameManager = new GameManager(
+ InstrumentationRegistry.getContext(), mService);
+ }
+
+ @Test
+ public void testGameModeGetterSetter() {
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ mGameManager.getGameMode(PACKAGE_NAME_0));
+
+ mGameManager.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ mGameManager.getGameMode(PACKAGE_NAME_1));
+
+ mGameManager.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE);
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+ mGameManager.getGameMode(PACKAGE_NAME_1));
+ }
+
+ private final class TestGameManagerService extends IGameManagerService.Stub {
+ private final ArrayMap<Pair<String, Integer>, Integer> mGameModes = new ArrayMap<>();
+
+ @Override
+ public @GameMode int getGameMode(String packageName, int userId) {
+ final Pair key = Pair.create(packageName, userId);
+ if (mGameModes.containsKey(key)) {
+ return mGameModes.get(key);
+ }
+ return GameManager.GAME_MODE_UNSUPPORTED;
+ }
+
+ @Override
+ public void setGameMode(String packageName, @GameMode int gameMode, int userId) {
+ mGameModes.put(Pair.create(packageName, userId), gameMode);
+ }
+ }
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bb826de..151a320 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -35,6 +35,8 @@
android:label="@string/permlab_testDenied"
android:description="@string/permdesc_testDenied" />
+ <uses-permission android:name="com.android.frameworks.coretests.permission.TEST_GRANTED" />
+
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
@@ -76,7 +78,6 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SMS"/>
- <uses-permission android:name="android.permission.TEST_GRANTED" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
@@ -1455,6 +1456,7 @@
</intent-filter>
</receiver>
<receiver android:name="android.app.activity.RemoteGrantedReceiver"
+ android:process=":remoteReceiver"
android:permission="com.android.frameworks.coretests.permission.TEST_GRANTED"
android:exported="true">
<intent-filter android:priority="2">
@@ -1462,6 +1464,7 @@
</intent-filter>
</receiver>
<receiver android:name="android.app.activity.RemoteDeniedReceiver"
+ android:process=":remoteReceiver"
android:permission="com.android.frameworks.coretests.permission.TEST_DENIED"
android:exported="true">
<intent-filter android:priority="2">
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
index 0c351d1..81eb213 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -132,7 +132,8 @@
true, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_RESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
TaskDescription td2 = new TaskDescription();
@@ -155,7 +156,8 @@
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
TaskDescription td2 = new TaskDescription(
@@ -169,7 +171,8 @@
true, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_RESIZEABLE, // resizeMode
102, // minWidth
- 202 // minHeight
+ 202, // minHeight
+ 0 // colorBackgroundFloating
);
// Must overwrite all public and hidden fields, since other has all fields set.
@@ -198,7 +201,8 @@
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
// Normal parceling should keep everything the same.
@@ -219,7 +223,8 @@
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
// Recycled bitmap will be ignored while parceling.
tdParcelled = new TaskDescription(parcelingRoundTrip(tdBitmapRecycled));
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 0f81896..d79c2fe 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -56,6 +56,8 @@
"com.android.frameworks.coretests.activity.BROADCAST_MULTI";
public static final String BROADCAST_ABORT =
"com.android.frameworks.coretests.activity.BROADCAST_ABORT";
+ public static final String BROADCAST_RESULT =
+ "com.android.frameworks.coretests.activity.BROADCAST_RESULT";
public static final String BROADCAST_STICKY1 =
"com.android.frameworks.coretests.activity.BROADCAST_STICKY1";
@@ -106,7 +108,14 @@
}
public Intent makeBroadcastIntent(String action) {
+ return makeBroadcastIntent(action, false);
+ }
+
+ public Intent makeBroadcastIntent(String action, boolean makeImplicit) {
Intent intent = new Intent(action, null);
+ if (makeImplicit) {
+ intent.addFlags(intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ }
intent.putExtra("caller", mCallTarget);
return intent;
}
@@ -277,7 +286,7 @@
map.putString("foo", "you");
map.putString("remove", "me");
getContext().sendOrderedBroadcast(
- new Intent("com.android.frameworks.coretests.activity.BROADCAST_RESULT"),
+ makeBroadcastIntent(BROADCAST_RESULT, true),
null, broadcastReceiver, null, 1, "foo", map);
while (!broadcastReceiver.mHaveResult) {
try {
@@ -424,10 +433,13 @@
public void testLocalReceivePermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED, true));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
+ /*
+ // TODO: multi-package test b/c self-target broadcasts are always allowed
+ // even when gated on ungranted permissions
public void testLocalReceivePermissionDenied() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_RESULTS});
@@ -438,16 +450,17 @@
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_LOCAL_DENIED),
+ makeBroadcastIntent(BROADCAST_LOCAL_DENIED, true),
null, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
+ */
public void testLocalBroadcastPermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
getContext().sendBroadcast(
- makeBroadcastIntent(BROADCAST_LOCAL),
+ makeBroadcastIntent(BROADCAST_LOCAL, true),
PERMISSION_GRANTED);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
@@ -462,7 +475,7 @@
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_LOCAL),
+ makeBroadcastIntent(BROADCAST_LOCAL, true),
PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
@@ -470,10 +483,13 @@
public void testRemoteReceivePermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED, true));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
+ /*
+ // TODO: multi-package test b/c self-target broadcasts are always allowed
+ // even when gated on ungranted permissions
public void testRemoteReceivePermissionDenied() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_RESULTS});
@@ -484,16 +500,17 @@
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_REMOTE_DENIED),
+ makeBroadcastIntent(BROADCAST_REMOTE_DENIED, true),
null, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
+ */
public void testRemoteBroadcastPermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
getContext().sendBroadcast(
- makeBroadcastIntent(BROADCAST_REMOTE),
+ makeBroadcastIntent(BROADCAST_REMOTE, true),
PERMISSION_GRANTED);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
@@ -508,7 +525,7 @@
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_REMOTE),
+ makeBroadcastIntent(BROADCAST_REMOTE, true),
PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
@@ -516,13 +533,13 @@
public void testReceiverCanNotRegister() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER, true));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
public void testReceiverCanNotBind() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND, true));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
diff --git a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
index 7662456..0b21fa9 100644
--- a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
+++ b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
@@ -253,16 +253,16 @@
sendBroadcast(makeBroadcastIntent(BROADCAST_REGISTERED));
} else if (BROADCAST_LOCAL.equals(action)) {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL));
+ sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL, true));
} else if (BROADCAST_REMOTE.equals(action)) {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
- sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE));
+ sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE, true));
} else if (BROADCAST_ALL.equals(action)) {
setExpectedReceivers(new String[]{
RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL});
registerMyReceiver(new IntentFilter(BROADCAST_ALL));
sCallingTest.addIntermediate("after-register");
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL, true), null);
} else if (BROADCAST_MULTI.equals(action)) {
setExpectedReceivers(new String[]{
RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
@@ -277,23 +277,26 @@
RECEIVER_REMOTE, RECEIVER_LOCAL});
registerMyReceiver(new IntentFilter(BROADCAST_ALL));
sCallingTest.addIntermediate("after-register");
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT), null);
+ final Intent allIntent = makeBroadcastIntent(BROADCAST_ALL, true);
+ final Intent localIntent = makeBroadcastIntent(BROADCAST_LOCAL, true);
+ final Intent remoteIntent = makeBroadcastIntent(BROADCAST_REMOTE, true);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(localIntent, null);
+ sendOrderedBroadcast(remoteIntent, null);
+ sendOrderedBroadcast(localIntent, null);
+ sendOrderedBroadcast(remoteIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT, true), null);
} else if (BROADCAST_ABORT.equals(action)) {
setExpectedReceivers(new String[]{
RECEIVER_REMOTE, RECEIVER_ABORT});
registerMyReceiver(new IntentFilter(BROADCAST_ABORT));
sCallingTest.addIntermediate("after-register");
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT, true), null);
} else if (BROADCAST_STICKY1.equals(action)) {
setExpectedReceivers(new String[]{RECEIVER_REG});
setExpectedData(new String[]{DATA_1});
@@ -436,7 +439,14 @@
}
private Intent makeBroadcastIntent(String action) {
+ return makeBroadcastIntent(action, false);
+ }
+
+ private Intent makeBroadcastIntent(String action, boolean makeImplicit) {
Intent intent = new Intent(action, null);
+ if (makeImplicit) {
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ }
intent.putExtra("caller", mCallTarget);
return intent;
}
diff --git a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
index 2120a1d..16ea73f 100644
--- a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
@@ -23,7 +23,7 @@
import android.os.IBinder;
import android.os.Parcel;
-class LocalDeniedReceiver extends BroadcastReceiver {
+public class LocalDeniedReceiver extends BroadcastReceiver {
public LocalDeniedReceiver() {
}
diff --git a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
index 7c89346..5c1ded9 100644
--- a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
@@ -23,7 +23,7 @@
import android.os.IBinder;
import android.os.Parcel;
-class RemoteDeniedReceiver extends BroadcastReceiver {
+public class RemoteDeniedReceiver extends BroadcastReceiver {
public RemoteDeniedReceiver() {
}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
new file mode 100644
index 0000000..af77b6c
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+
+public class GenericDocumentTest {
+ @Test
+ public void testRecreateFromParcel() {
+ GenericDocument inDoc =
+ new GenericDocument.Builder<>("uri1", "schema1")
+ .setScore(42)
+ .setPropertyString("propString", "Hello")
+ .setPropertyBytes("propBytes", new byte[][] {{1, 2}})
+ .setPropertyDocument(
+ "propDocument",
+ new GenericDocument.Builder<>("uri2", "schema2")
+ .setPropertyString("propString", "Goodbye")
+ .setPropertyBytes("propBytes", new byte[][] {{3, 4}})
+ .build())
+ .build();
+
+ // Serialize the document
+ Parcel inParcel = Parcel.obtain();
+ inParcel.writeBundle(inDoc.getBundle());
+ byte[] data = inParcel.marshall();
+ inParcel.recycle();
+
+ // Deserialize the document
+ Parcel outParcel = Parcel.obtain();
+ outParcel.unmarshall(data, 0, data.length);
+ outParcel.setDataPosition(0);
+ Bundle outBundle = outParcel.readBundle();
+ outParcel.recycle();
+
+ // Compare results
+ GenericDocument outDoc = new GenericDocument(outBundle);
+ assertThat(inDoc).isEqualTo(outDoc);
+ assertThat(outDoc.getPropertyString("propString")).isEqualTo("Hello");
+ assertThat(outDoc.getPropertyBytesArray("propBytes")).isEqualTo(new byte[][] {{1, 2}});
+ assertThat(outDoc.getPropertyDocument("propDocument").getPropertyString("propString"))
+ .isEqualTo("Goodbye");
+ assertThat(outDoc.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes"))
+ .isEqualTo(new byte[][] {{3, 4}});
+ }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
index 986079f..7d175d9 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
@@ -34,9 +34,9 @@
new AppSearchEmail.Builder("test1").build(),
new AppSearchEmail.Builder("test2").build());
PutDocumentsRequest request =
- new PutDocumentsRequest.Builder().addGenericDocument(emails).build();
+ new PutDocumentsRequest.Builder().addGenericDocuments(emails).build();
- assertThat(request.getDocuments().get(0).getUri()).isEqualTo("test1");
- assertThat(request.getDocuments().get(1).getUri()).isEqualTo("test2");
+ assertThat(request.getGenericDocuments().get(0).getUri()).isEqualTo("test1");
+ assertThat(request.getGenericDocuments().get(1).getUri()).isEqualTo("test2");
}
}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index c8cee85..0fe44630 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -33,8 +33,8 @@
SearchSpec searchSpec =
new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .addNamespace("namespace1", "namespace2")
- .addSchemaType("schemaTypes1", "schemaTypes2")
+ .addFilterNamespaces("namespace1", "namespace2")
+ .addFilterSchemas("schemaTypes1", "schemaTypes2")
.addFilterPackageNames("package1", "package2")
.setSnippetCount(5)
.setSnippetCountPerProperty(10)
@@ -49,7 +49,7 @@
.isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD))
.containsExactly("namespace1", "namespace2");
- assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD))
+ assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_FIELD))
.containsExactly("schemaTypes1", "schemaTypes2");
assertThat(bundle.getStringArrayList(SearchSpec.PACKAGE_NAME_FIELD))
.containsExactly("package1", "package2");
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
index aab9229..bf6b07f 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
@@ -75,12 +75,12 @@
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
// By default, the schema is visible.
- SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(schema).build();
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
.setSchemaTypeVisibilityForSystemUi("Schema", true)
.build();
assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
@@ -91,7 +91,7 @@
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
SetSchemaRequest request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
.setSchemaTypeVisibilityForSystemUi("Schema", false)
.build();
assertThat(request.getSchemasNotVisibleToSystemUi()).containsExactly("Schema");
@@ -102,7 +102,7 @@
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
// By default, the schema is not visible.
- SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(schema).build();
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
assertThat(request.getSchemasVisibleToPackages()).isEmpty();
PackageIdentifier packageIdentifier =
@@ -112,7 +112,7 @@
request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
.setSchemaTypeVisibilityForPackage(
"Schema", /*visible=*/ true, packageIdentifier)
.build();
@@ -126,7 +126,7 @@
SetSchemaRequest request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
.setSchemaTypeVisibilityForPackage(
"Schema",
/*visible=*/ false,
@@ -147,7 +147,7 @@
SetSchemaRequest request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
// Set it visible for "Schema"
.setSchemaTypeVisibilityForPackage(
"Schema", /*visible=*/ true, packageIdentifier)
@@ -165,7 +165,7 @@
SetSchemaRequest request =
new SetSchemaRequest.Builder()
- .addSchema(schema)
+ .addSchemas(schema)
// First set it as visible
.setSchemaTypeVisibilityForPackage(
"Schema",
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 88faa0a..57c0be5 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -78,6 +78,7 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -334,7 +335,8 @@
private ParsingPackage parsePackage(Uri packageURI) {
final String archiveFilePath = packageURI.getPath();
ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(
- new File(archiveFilePath), 0 /*flags*/, false /*collectCertificates*/);
+ new File(archiveFilePath), 0 /*flags*/, Collections.emptyList(),
+ false /*collectCertificates*/);
if (result.isError()) {
throw new IllegalStateException(result.getErrorMessage(), result.getException());
}
diff --git a/core/tests/coretests/src/android/content/pm/SignatureTest.java b/core/tests/coretests/src/android/content/pm/SignatureTest.java
index f0b4af6..19458da 100644
--- a/core/tests/coretests/src/android/content/pm/SignatureTest.java
+++ b/core/tests/coretests/src/android/content/pm/SignatureTest.java
@@ -54,6 +54,32 @@
assertFalse(Signature.areEffectiveMatch(asArray(A, M), asArray(A, B)));
}
+ public void testHashCode_doesNotIncludeFlags() throws Exception {
+ // Some classes rely on the hash code not including the flags / capabilities for the signer
+ // to verify Set membership. This test verifies two signers with the same signature but
+ // different flags have the same hash code.
+ Signature signatureAWithAllCaps = new Signature(A.toCharsString());
+ // There are currently 5 capabilities that can be assigned to a previous signer, although
+ // for the purposes of this test all that matters is that the two flag values are distinct.
+ signatureAWithAllCaps.setFlags(31);
+ Signature signatureAWithNoCaps = new Signature(A.toCharsString());
+ signatureAWithNoCaps.setFlags(0);
+
+ assertEquals(signatureAWithAllCaps.hashCode(), signatureAWithNoCaps.hashCode());
+ }
+
+ public void testEquals_doesNotIncludeFlags() throws Exception {
+ // Similar to above some classes rely on equals only comparing the signature arrays
+ // for equality without including the flags. This test verifies two signers with the
+ // same signature but different flags are still considered equal.
+ Signature signatureAWithAllCaps = new Signature(A.toCharsString());
+ signatureAWithAllCaps.setFlags(31);
+ Signature signatureAWithNoCaps = new Signature(A.toCharsString());
+ signatureAWithNoCaps.setFlags(0);
+
+ assertEquals(signatureAWithAllCaps, signatureAWithNoCaps);
+ }
+
private static Signature[] asArray(Signature... s) {
return s;
}
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
index b319886..341ee37 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
@@ -149,7 +149,7 @@
0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */,
false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
- true /* hasSensor */);
+ true /* hasSensor */, false /* hasBattery */);
assertTrue(d.hasSensor());
return d;
}
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
index 6955ca8..564103e 100644
--- a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
@@ -117,6 +117,61 @@
}
@Test
+ public void testDurationMono() {
+ assertEquals(1, CombinedVibrationEffect.createSynced(
+ VibrationEffect.createOneShot(1, 1)).getDuration());
+ assertEquals(-1, CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration());
+ assertEquals(Long.MAX_VALUE, CombinedVibrationEffect.createSynced(
+ VibrationEffect.createWaveform(
+ new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0)).getDuration());
+ }
+
+ @Test
+ public void testDurationStereo() {
+ assertEquals(6, CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.createOneShot(1, 1))
+ .addVibrator(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1))
+ .combine()
+ .getDuration());
+ assertEquals(-1, CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1))
+ .combine()
+ .getDuration());
+ assertEquals(Long.MAX_VALUE, CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0))
+ .combine()
+ .getDuration());
+ }
+
+ @Test
+ public void testDurationSequential() {
+ assertEquals(26, CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.createOneShot(10, 10), 10)
+ .addNext(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1))
+ .combine()
+ .getDuration());
+ assertEquals(-1, CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addNext(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1))
+ .combine()
+ .getDuration());
+ assertEquals(Long.MAX_VALUE, CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addNext(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0))
+ .combine()
+ .getDuration());
+ }
+
+ @Test
public void testSerializationMono() {
CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(VALID_EFFECT);
diff --git a/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
new file mode 100644
index 0000000..be38260
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ContentValues;
+import android.os.Parcel;
+import android.provider.SimPhonebookContract.SimRecords.NameValidationResult;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SimPhonebookContractTest {
+
+ @Test
+ public void getContentUri_invalidEfType_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getContentUri(1, 100)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getContentUri(1, -1)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getContentUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_UNKNOWN)
+ );
+ }
+
+ @Test
+ public void getItemUri_invalidEfType_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1, 100, 1)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1, -1, 1)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_UNKNOWN, 1)
+ );
+ }
+
+ @Test
+ public void getItemUri_invalidRecordIndex_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_ADN, 0)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_ADN, -1)
+ );
+ }
+
+ @Test
+ public void nameValidationResult_isValid_validNames() {
+ assertThat(new NameValidationResult("", "", 0, 1).isValid()).isTrue();
+ assertThat(new NameValidationResult("a", "a", 1, 1).isValid()).isTrue();
+ assertThat(new NameValidationResult("First Last", "First Last", 10, 10).isValid()).isTrue();
+ assertThat(
+ new NameValidationResult("First Last", "First Last", 10, 100).isValid()).isTrue();
+ }
+
+ @Test
+ public void nameValidationResult_isValid_invalidNames() {
+ assertThat(new NameValidationResult("", "", 0, 0).isValid()).isFalse();
+ assertThat(new NameValidationResult("ab", "ab", 2, 1).isValid()).isFalse();
+ NameValidationResult unsupportedCharactersResult = new NameValidationResult("A_b_c",
+ "A b c", 5, 5);
+ assertThat(unsupportedCharactersResult.isValid()).isFalse();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(0)).isTrue();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(1)).isFalse();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(2)).isTrue();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(3)).isFalse();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(4)).isTrue();
+ }
+
+ @Test
+ public void nameValidationResult_parcel() {
+ ContentValues values = new ContentValues();
+ values.put("name", "Name");
+ values.put("phone_number", "123");
+
+ NameValidationResult result;
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeParcelable(new NameValidationResult("name", "sanitized name", 1, 2), 0);
+ parcel.setDataPosition(0);
+ result = parcel.readParcelable(NameValidationResult.class.getClassLoader());
+ } finally {
+ parcel.recycle();
+ }
+
+ assertThat(result.getName()).isEqualTo("name");
+ assertThat(result.getSanitizedName()).isEqualTo("sanitized name");
+ assertThat(result.getEncodedLength()).isEqualTo(1);
+ assertThat(result.getMaxEncodedLength()).isEqualTo(2);
+ }
+}
+
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 9705284..cfe3803 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -27,6 +27,10 @@
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
@@ -427,6 +431,27 @@
cutout.getBoundingRectBottom());
}
+ @Test
+ public void testCalculateRelativeRoundedCorners() {
+ mState.setDisplayFrame(new Rect(0, 0, 200, 400));
+ mState.setRoundedCorners(new RoundedCorners(
+ new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10),
+ new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10),
+ new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
+ new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
+ WindowInsets windowInsets = mState.calculateInsets(new Rect(1, 2, 197, 396), null, false,
+ false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
+ WINDOWING_MODE_UNDEFINED, new SparseIntArray());
+ assertEquals(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8),
+ windowInsets.getRoundedCorner(POSITION_TOP_LEFT));
+ assertEquals(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8),
+ windowInsets.getRoundedCorner(POSITION_TOP_RIGHT));
+ assertEquals(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 179, 378),
+ windowInsets.getRoundedCorner(POSITION_BOTTOM_RIGHT));
+ assertEquals(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 19, 378),
+ windowInsets.getRoundedCorner(POSITION_BOTTOM_LEFT));
+ }
+
private void assertEqualsAndHashCode() {
assertEquals(mState, mState2);
assertEquals(mState.hashCode(), mState2.hashCode());
diff --git a/core/tests/coretests/src/android/view/RoundedCornerTest.java b/core/tests/coretests/src/android/view/RoundedCornerTest.java
new file mode 100644
index 0000000..8eb13bc
--- /dev/null
+++ b/core/tests/coretests/src/android/view/RoundedCornerTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.view;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Point;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RoundedCornerTest {
+
+ @Test
+ public void testGetPosition() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner.getPosition(), is(RoundedCorner.POSITION_BOTTOM_LEFT));
+ }
+
+ @Test
+ public void testGetRadius() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner.getRadius(), is(2));
+ }
+
+ @Test
+ public void testGetCenter() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner.getCenter(), equalTo(new Point(3, 4)));
+ }
+
+ @Test
+ public void testIsEmpty() {
+ RoundedCorner roundedCorner = new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+ assertThat(roundedCorner.isEmpty(), is(true));
+ }
+
+ @Test
+ public void testEquals() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ RoundedCorner roundedCorner2 = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner, equalTo(roundedCorner2));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/RoundedCornersTest.java b/core/tests/coretests/src/android/view/RoundedCornersTest.java
new file mode 100644
index 0000000..07ef33a
--- /dev/null
+++ b/core/tests/coretests/src/android/view/RoundedCornersTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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.view;
+
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RoundedCornersTest {
+
+ final RoundedCorners mRoundedCorners = new RoundedCorners(
+ new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10),
+ new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10),
+ new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
+ new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380));
+
+ @Test
+ public void testGetRoundedCorner() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
+ }
+
+ @Test
+ public void testGetRoundedCorner_noRoundedCorners() {
+ RoundedCorners roundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT), nullValue());
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT), nullValue());
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT), nullValue());
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), nullValue());
+ }
+
+ @Test
+ public void testHashCode() {
+ assertThat(mRoundedCorners.hashCode(),
+ equalTo(RoundedCorners.fromRadii(new Pair<>(10, 20), 200, 400).hashCode()));
+ assertThat(mRoundedCorners.hashCode(),
+ not(equalTo(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400).hashCode())));
+ }
+
+ @Test
+ public void testEquals() {
+ assertThat(mRoundedCorners,
+ equalTo(RoundedCorners.fromRadii(new Pair<>(10, 20), 200, 400)));
+
+ assertThat(mRoundedCorners,
+ not(equalTo(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400))));
+ }
+
+ @Test
+ public void testSetRoundedCorner() {
+ RoundedCorner roundedCorner = new RoundedCorner(POSITION_BOTTOM_LEFT, 5, 6, 7);
+ mRoundedCorners.setRoundedCorner(POSITION_BOTTOM_LEFT, roundedCorner);
+
+ assertThat(mRoundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), equalTo(roundedCorner));
+ }
+
+ @Test
+ public void testSetRoundedCorner_null() {
+ mRoundedCorners.setRoundedCorner(POSITION_BOTTOM_LEFT, null);
+
+ assertThat(mRoundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT],
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT)));
+ assertThat(mRoundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), nullValue());
+ }
+
+ @Test
+ public void testInsetRoundedCorners_partialOverlap() {
+ RoundedCorners roundedCorners = mRoundedCorners.inset(1, 2, 3, 4);
+
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_LEFT],
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8)));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_RIGHT],
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8)));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_RIGHT],
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 179, 378)));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT],
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 19, 378)));
+ }
+
+ @Test
+ public void testInsetRoundedCorners_noOverlap() {
+ RoundedCorners roundedCorners = mRoundedCorners.inset(20, 20, 20, 20);
+
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_LEFT].isEmpty(), is(true));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_RIGHT].isEmpty(), is(true));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_RIGHT].isEmpty(), is(true));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT].isEmpty(), is(true));
+ }
+
+ @Test
+ public void testRotateRoundedCorners_90() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400)
+ .rotate(ROTATION_90, 200, 400);
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 20, 380, 20)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 380, 180)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 10, 10, 190)));
+ }
+
+ @Test
+ public void testRotateRoundedCorners_270() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400)
+ .rotate(ROTATION_270, 200, 400);
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 20, 20, 20)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 390, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 10, 390, 190)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 180)));
+ }
+
+ @Test
+ public void testFromRadius_cache() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(radius, 200, 400), sameInstance(cached));
+ }
+
+ @Test
+ public void testFromRadius_wontCacheIfRadiusChanged() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400),
+ not(sameInstance(cached)));
+ }
+
+ @Test
+ public void testFromRadius_wontCacheIfDisplayWidthChanged() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(radius, 100, 400),
+ not(sameInstance(cached)));
+ }
+
+ @Test
+ public void testFromRadius_wontCacheIfDisplayHeightChanged() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(radius, 200, 300),
+ not(sameInstance(cached)));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 8e4e735..b80837f 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -61,7 +61,7 @@
WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0));
WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0));
WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, false, null,
- systemBars(), true /* compatIgnoreVisibility */);
+ null, systemBars(), true /* compatIgnoreVisibility */);
assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets());
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 96e9c4a..7b1ca8f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -73,7 +73,6 @@
final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo");
- uidBatteryConsumerBuilder.setConsumedPower(200);
uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300);
uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
@@ -90,7 +89,6 @@
final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
builder.getOrCreateSystemBatteryConsumerBuilder(
SystemBatteryConsumer.DRAIN_TYPE_CAMERA);
- systemBatteryConsumerBuilder.setConsumedPower(10000);
systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100);
systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
@@ -114,7 +112,6 @@
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
if (uidBatteryConsumer.getUid() == 2000) {
assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo");
- assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(200);
assertThat(uidBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300);
assertThat(uidBatteryConsumer.getConsumedPower(
@@ -130,6 +127,7 @@
BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700);
assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800);
+ assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1710);
} else {
fail("Unexpected UID " + uidBatteryConsumer.getUid());
}
@@ -139,7 +137,6 @@
batteryUsageStats.getSystemBatteryConsumers();
for (SystemBatteryConsumer systemBatteryConsumer : systemBatteryConsumers) {
if (systemBatteryConsumer.getDrainType() == SystemBatteryConsumer.DRAIN_TYPE_CAMERA) {
- assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(10000);
assertThat(systemBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
@@ -151,6 +148,7 @@
BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300);
assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400);
+ assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(30510);
} else {
fail("Unexpected drain type " + systemBatteryConsumer.getDrainType());
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index bf74c8d..1687a78 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -47,9 +47,10 @@
setExternalStatsSyncLocked(new DummyExternalStatsSync());
- final boolean[] supportedBuckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
- Arrays.fill(supportedBuckets, true);
- mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedBuckets);
+ final boolean[] supportedStandardBuckets
+ = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS];
+ Arrays.fill(supportedStandardBuckets, true);
+ mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedStandardBuckets, 2);
// A no-op handler.
mHandler = new Handler(Looper.getMainLooper()) {
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index c9c81ac..b9908f4 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -21,10 +21,11 @@
import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE;
import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON;
import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER;
-import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS;
+import static com.android.internal.power.MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
@@ -47,60 +48,77 @@
@Test
public void testConstruction() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (supportedEnergyBuckets[i]) {
- assertTrue(stats.isEnergyBucketSupported(i));
- assertEquals(0L, stats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ if (supportedStandardBuckets[i]) {
+ assertTrue(stats.isStandardBucketSupported(i));
+ assertEquals(0L, stats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertFalse(stats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(i));
+ assertFalse(stats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketEnergy(i));
}
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(0L, stats.getAccumulatedCustomBucketEnergy(i));
+ }
}
@Test
public void testCreateFromTemplate() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (supportedEnergyBuckets[i]) {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(0, newStats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ if (supportedStandardBuckets[i]) {
+ assertTrue(newStats.isStandardBucketSupported(i));
+ assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertFalse(newStats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
+ assertFalse(newStats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(i));
+ }
}
@Test
public void testReadWriteParcel() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
final Parcel parcel = Parcel.obtain();
stats.writeToParcel(parcel);
@@ -108,94 +126,127 @@
parcel.setDataPosition(0);
MeasuredEnergyStats newStats = new MeasuredEnergyStats(parcel);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(i),
+ newStats.getAccumulatedCustomBucketEnergy(i));
+ }
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1));
parcel.recycle();
}
@Test
public void testCreateAndReadSummaryFromParcel() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
parcel.setDataPosition(0);
MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- assertEquals(stats.isEnergyBucketSupported(i),
- newStats.isEnergyBucketSupported(i));
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ assertEquals(stats.isStandardBucketSupported(i),
+ newStats.isStandardBucketSupported(i));
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(i),
+ newStats.getAccumulatedCustomBucketEnergy(i));
+ }
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1));
parcel.recycle();
}
@Test
public void testCreateAndReadSummaryFromParcel_existingTemplate() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats template = new MeasuredEnergyStats(supportedEnergyBuckets);
- template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- template.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats template
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ template.updateCustomBucket(0, 50, true);
final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 7, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
+ stats.updateCustomBucket(0, 315, true);
+ stats.updateCustomBucket(1, 316, true);
final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
- final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched from false to true
- newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true to false
- final MeasuredEnergyStats newTemplate = new MeasuredEnergyStats(newSupportedEnergyBuckets);
+ final boolean[] newsupportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched false > true
+ newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true > false
+ final MeasuredEnergyStats newTemplate
+ = new MeasuredEnergyStats(newsupportedStandardBuckets, numCustomBuckets);
parcel.setDataPosition(0);
final MeasuredEnergyStats newStats =
MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (!newSupportedEnergyBuckets[i]) {
- assertFalse(newStats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
- } else if (!supportedEnergyBuckets[i]) {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ if (!newsupportedStandardBuckets[i]) {
+ assertFalse(newStats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedStandardBucketEnergy(i));
+ } else if (!supportedStandardBuckets[i]) {
+ assertTrue(newStats.isStandardBucketSupported(i));
+ assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ assertTrue(newStats.isStandardBucketSupported(i));
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(i),
+ newStats.getAccumulatedCustomBucketEnergy(i));
+ }
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1));
parcel.recycle();
}
@Test
public void testCreateAndReadSummaryFromParcel_skipZero() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- Arrays.fill(supportedEnergyBuckets, true);
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ Arrays.fill(supportedStandardBuckets, true);
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- // Accumulate energy in one bucket, the rest should be zero
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ // Accumulate energy in one bucket and one custom bucket, the rest should be zero
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+ stats.updateCustomBucket(1, 60, true);
+ // Let's try parcelling with including zeros
final Parcel includeZerosParcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false);
includeZerosParcel.setDataPosition(0);
@@ -203,90 +254,180 @@
MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
includeZerosParcel);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
if (i == ENERGY_BUCKET_SCREEN_ON) {
- assertEquals(stats.isEnergyBucketSupported(i),
- newStats.isEnergyBucketSupported(i));
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ assertEquals(stats.isStandardBucketSupported(i),
+ newStats.isStandardBucketSupported(i));
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
+ assertTrue(newStats.isStandardBucketSupported(i));
+ assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
}
}
+ assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0));
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(1),
+ newStats.getAccumulatedCustomBucketEnergy(1));
includeZerosParcel.recycle();
+ // Now let's try parcelling with skipping zeros
final Parcel skipZerosParcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true);
skipZerosParcel.setDataPosition(0);
newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
if (i == ENERGY_BUCKET_SCREEN_ON) {
- assertEquals(stats.isEnergyBucketSupported(i),
- newStats.isEnergyBucketSupported(i));
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ assertEquals(stats.isStandardBucketSupported(i),
+ newStats.isStandardBucketSupported(i));
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertFalse(newStats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
+ assertFalse(newStats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
}
+ assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0));
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(1),
+ newStats.getAccumulatedCustomBucketEnergy(1));
skipZerosParcel.recycle();
}
@Test
+ public void testCreateAndReadSummaryFromParcel_nullTemplate() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
+
+ final Parcel parcel = Parcel.obtain();
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
+ parcel.setDataPosition(0);
+
+ MeasuredEnergyStats newStats
+ = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, null);
+ assertNull(newStats);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testCreateAndReadSummaryFromParcel_boring() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+
+ final MeasuredEnergyStats template
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ template.updateCustomBucket(0, 50, true);
+
+ final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L, true);
+
+ final Parcel parcel = Parcel.obtain();
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
+
+ final boolean[] newSupportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched false > true
+ newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true > false
+ final MeasuredEnergyStats newTemplate
+ = new MeasuredEnergyStats(newSupportedStandardBuckets, numCustomBuckets);
+ parcel.setDataPosition(0);
+
+ final MeasuredEnergyStats newStats =
+ MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate);
+ // The only non-0 entry in stats is no longer supported, so now there's no interesting data.
+ assertNull(newStats);
+ assertEquals("Parcel was not properly consumed", 0, parcel.dataAvail());
+ parcel.recycle();
+ }
+
+ @Test
public void testUpdateBucket() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- assertEquals(15, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
+ stats.updateCustomBucket(0, 3, true);
+
+ assertEquals(15, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
assertEquals(ENERGY_DATA_UNAVAILABLE,
- stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE));
- assertEquals(40, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER));
+ stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE));
+ assertEquals(40, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER));
+ assertEquals(50 + 3, stats.getAccumulatedCustomBucketEnergy(0));
+ assertEquals(60, stats.getAccumulatedCustomBucketEnergy(1));
}
@Test
public void testReset() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
MeasuredEnergyStats.resetIfNotNull(stats);
// All energy should be reset to 0
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (supportedEnergyBuckets[i]) {
- assertTrue(stats.isEnergyBucketSupported(i));
- assertEquals(0, stats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ if (supportedStandardBuckets[i]) {
+ assertTrue(stats.isStandardBucketSupported(i));
+ assertEquals(0, stats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertFalse(stats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(i));
+ assertFalse(stats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketEnergy(i));
}
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(0, stats.getAccumulatedCustomBucketEnergy(i));
+ }
// Values should increase as usual.
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 70, true);
- assertEquals(70L, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70, true);
+ assertEquals(70L, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
+
+ stats.updateCustomBucket(1, 12, true);
+ assertEquals(12L, stats.getAccumulatedCustomBucketEnergy(1));
}
/** Test that states are mapped to the expected energy buckets. Beware of mapping changes. */
@Test
- public void testEnergyBucketMapping() {
+ public void testStandardBucketMapping() {
int exp;
exp = ENERGY_BUCKET_SCREEN_ON;
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
new file mode 100644
index 0000000..5a3ea35
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
@@ -0,0 +1,527 @@
+/*
+ * 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.view;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
+import android.util.DisplayMetrics;
+import android.view.DisplayAdjustments.FixedRotationAdjustments;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.quality.Strictness;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link Display}.
+ *
+ * <p>Build/Install/Run:
+ *
+ * atest FrameworksMockingCoreTests:android.view.DisplayTests
+ */
+@RunWith(AndroidJUnit4.class)
+public class DisplayTests {
+
+ private static final int APP_WIDTH = 272;
+ private static final int APP_HEIGHT = 700;
+ // Tablet size device, ROTATION_0 corresponds to portrait.
+ private static final int LOGICAL_WIDTH = 700;
+ private static final int LOGICAL_HEIGHT = 1800;
+
+ // Bounds of the app when the device is in portrait mode.
+ private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
+ private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
+
+ private StaticMockitoSession mMockitoSession;
+
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+ private Context mApplicationContext;
+ private DisplayInfo mDisplayInfo = new DisplayInfo();
+
+ @Before
+ public void setupTests() {
+ mMockitoSession = mockitoSession()
+ .mockStatic(DisplayManagerGlobal.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ // Ensure no adjustments are set before each test.
+ mApplicationContext = ApplicationProvider.getApplicationContext();
+ DisplayAdjustments displayAdjustments =
+ mApplicationContext.getResources().getDisplayAdjustments();
+ displayAdjustments.setFixedRotationAdjustments(null);
+ mApplicationContext.getResources().overrideDisplayAdjustments(null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
+ null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
+ null);
+ mDisplayInfo.rotation = ROTATION_0;
+
+ mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
+ doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
+ }
+
+ @After
+ public void teardownTests() {
+ if (mMockitoSession != null) {
+ mMockitoSession.finishMocking();
+ }
+ Mockito.framework().clearInlineMocks();
+ }
+
+ @Test
+ public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testConstructor_defaultResources_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ mApplicationContext.getResources().getDisplayAdjustments());
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ // GIVEN display is constructed with display adjustments.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ displayAdjustments);
+ // THEN rotation is not adjusted since no override was set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is not adjusted since no override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is adjusted since an override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_90);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated with an override.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
+ }
+
+ // Given rotated display dimensions, calculate the letterboxed app bounds.
+ private static Rect buildAppBounds(int displayWidth, int displayHeight) {
+ final int midWidth = displayWidth / 2;
+ final int left = midWidth - (APP_WIDTH / 2);
+ final int right = midWidth + (APP_WIDTH / 2);
+ final int midHeight = displayHeight / 2;
+ // Coordinate system starts at top left.
+ final int top = midHeight - (APP_HEIGHT / 2);
+ final int bottom = midHeight + (APP_HEIGHT / 2);
+ return new Rect(left, top, right, bottom);
+ }
+
+ private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_90;
+ // Flip width & height assignment since the device is rotated.
+ displayInfo.logicalWidth = LOGICAL_HEIGHT;
+ displayInfo.logicalHeight = LOGICAL_WIDTH;
+ }
+
+ private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_0;
+ displayInfo.logicalWidth = LOGICAL_WIDTH;
+ displayInfo.logicalHeight = LOGICAL_HEIGHT;
+ }
+
+ /**
+ * Set max bounds to be sandboxed to the app bounds, indicating the app is in
+ * size compat mode or letterbox.
+ */
+ private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
+ resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
+ }
+
+ /**
+ * Do not compare entire display info, since it is updated to match display the test is run on.
+ */
+ private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
+ assertThat(actual.displayId).isEqualTo(expected.displayId);
+ assertThat(actual.rotation).isEqualTo(expected.rotation);
+ assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
+ assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeIsLandscape(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ // Flip the width and height check since the device is rotated.
+ assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
+ }
+
+ private static void verifyRealMetricsIsLandscape(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ // Flip the width and height check since the device is rotated.
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
+ }
+
+ private static void verifyRealSizeIsPortrait(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
+ }
+
+ private static void verifyRealMetricsIsPortrait(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
+ }
+
+ private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
+ assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
+ }
+
+ private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
+ Resources resources, @Surface.Rotation int rotation) {
+ FixedRotationAdjustments fixedRotationAdjustments =
+ setFixedRotationAdjustments(resources, rotation);
+ resources.overrideDisplayAdjustments(
+ buildOverrideRotationAdjustments(fixedRotationAdjustments));
+ return fixedRotationAdjustments;
+ }
+
+ private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
+ @Surface.Rotation int rotation) {
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
+ return fixedRotationAdjustments;
+ }
+
+ private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
+ FixedRotationAdjustments fixedRotationAdjustments) {
+ return consumedDisplayAdjustments
+ -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ }
+}
diff --git a/core/tests/mockingcoretests/src/android/view/OWNERS b/core/tests/mockingcoretests/src/android/view/OWNERS
new file mode 100644
index 0000000..9c9f824
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/view/OWNERS
@@ -0,0 +1,2 @@
+# Display
+per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
index ebbdda5..2d7d9b4 100644
--- a/core/tests/overlaytests/device/AndroidTest.xml
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -19,17 +19,13 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="remount-system" value="true" />
<option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" />
</target_preparer>
-
- <!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. -->
- <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
- <option name="pre-reboot" value="true" />
- <option name="post-reboot" value="true" />
- </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="cleanup-apks" value="true" />
diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml
index 99c38db..598d202 100644
--- a/data/etc/com.android.launcher3.xml
+++ b/data/etc/com.android.launcher3.xml
@@ -21,5 +21,8 @@
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
+ <permission name="android.permission.START_TASKS_FROM_RECENTS"/>
+ <permission name="android.permission.STATUS_BAR"/>
+ <permission name="android.permission.STOP_APP_SWITCHES"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 301b491..b0ae9b9 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -223,14 +223,6 @@
targetSdk="29">
<new-permission name="android.permission.ACCESS_MEDIA_LOCATION" />
</split-permission>
- <split-permission name="android.permission.RECORD_AUDIO"
- targetSdk="31">
- <new-permission name="android.permission.RECORD_BACKGROUND_AUDIO" />
- </split-permission>
- <split-permission name="android.permission.CAMERA"
- targetSdk="31">
- <new-permission name="android.permission.BACKGROUND_CAMERA" />
- </split-permission>
<!-- This is a list of all the libraries available for application
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f84d947..ae8e3ce 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -355,7 +355,6 @@
<permission name="android.permission.MOVE_PACKAGE"/>
<!-- Needed for test only -->
<permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
- <permission name="android.permission.NETWORK_AIRPLANE_MODE"/>
<permission name="android.permission.OBSERVE_APP_USAGE"/>
<permission name="android.permission.NETWORK_SCAN"/>
<permission name="android.permission.PACKAGE_USAGE_STATS" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 222c9bd..ac8a296 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1903,12 +1903,6 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "123161180": {
- "message": "SEVER CHILDREN",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
- },
"140319294": {
"message": "IME target changed within ActivityRecord",
"level": "DEBUG",
@@ -2143,6 +2137,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "332390227": {
+ "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_CONFIGURATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2623,6 +2623,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
},
+ "910200295": {
+ "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_CONFIGURATION",
+ "at": "com\/android\/server\/wm\/Task.java"
+ },
"913494177": {
"message": "removeAllWindowsIfPossible: removing win=%s",
"level": "WARN",
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index c1310a9..4a92cf1 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -693,6 +693,32 @@
throw new IllegalArgumentException("Unrecognized outline?");
}
+ /** @hide */
+ public boolean clearStretch() {
+ return nClearStretch(mNativeRenderNode);
+ }
+
+ /** @hide */
+ public boolean stretch(float left, float top, float right, float bottom,
+ float vecX, float vecY, float maxStretchAmount) {
+ if (1.0 < vecX || vecX < -1.0) {
+ throw new IllegalArgumentException("vecX must be in the range [-1, 1], was " + vecX);
+ }
+ 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) {
+ throw new IllegalArgumentException(
+ "Stretch region must not be empty, got "
+ + new RectF(left, top, right, bottom).toString());
+ }
+ if (maxStretchAmount <= 0.0f) {
+ throw new IllegalArgumentException(
+ "The max stretch amount must be >0, got " + maxStretchAmount);
+ }
+ return nStretch(mNativeRenderNode, left, top, right, bottom, vecX, vecY, maxStretchAmount);
+ }
+
/**
* Checks if the RenderNode has a shadow. That is, if the combination of {@link #getElevation()}
* and {@link #getTranslationZ()} is greater than zero, there is an {@link Outline} set with
@@ -1638,6 +1664,13 @@
private static native boolean nSetOutlineNone(long renderNode);
@CriticalNative
+ private static native boolean nClearStretch(long renderNode);
+
+ @CriticalNative
+ private static native boolean nStretch(long renderNode, float left, float top, float right,
+ float bottom, float vecX, float vecY, float maxStretch);
+
+ @CriticalNative
private static native boolean nHasShadow(long renderNode);
@CriticalNative
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 7f22dc2..28b3b04 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1465,24 +1465,6 @@
}
/**
- * Notifies the drawable that it has been attached.
- *
- * @param v The view that it is attached to
- * @hide
- */
- public void onAttached(View v) {
- }
-
- /**
- * Notifies the drawable that it has been detached.
- *
- * @param v The view that it is detached from
- * @hide
- */
- public void onDetached(View v) {
- }
-
- /**
* Sets the source override density for this Drawable. If non-zero, this density is to be used
* for any calls to {@link Resources#getDrawableForDensity(int, int, Theme)} or
* {@link Resources#getValueForDensity(int, int, TypedValue, boolean)}.
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index f41b608..ae9f8664 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -19,9 +19,9 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
-import com.android.org.bouncycastle.util.io.pem.PemObject;
-import com.android.org.bouncycastle.util.io.pem.PemReader;
-import com.android.org.bouncycastle.util.io.pem.PemWriter;
+import com.android.internal.org.bouncycastle.util.io.pem.PemObject;
+import com.android.internal.org.bouncycastle.util.io.pem.PemReader;
+import com.android.internal.org.bouncycastle.util.io.pem.PemWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 4a67135..e19d88c 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -45,8 +45,8 @@
import android.security.keystore.UserNotAuthenticatedException;
import android.util.Log;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import java.io.ByteArrayInputStream;
import java.io.IOException;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 6ad8d2c..334b111 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -26,24 +26,24 @@
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keymaster.KeymasterDefs;
-import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.org.bouncycastle.asn1.ASN1Integer;
-import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import com.android.org.bouncycastle.asn1.DERBitString;
-import com.android.org.bouncycastle.asn1.DERNull;
-import com.android.org.bouncycastle.asn1.DERSequence;
-import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import com.android.org.bouncycastle.asn1.x509.Certificate;
-import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.org.bouncycastle.asn1.x509.TBSCertificate;
-import com.android.org.bouncycastle.asn1.x509.Time;
-import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
-import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import com.android.org.bouncycastle.jce.X509Principal;
-import com.android.org.bouncycastle.jce.provider.X509CertificateObject;
-import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.DERNull;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.TBSCertificate;
+import com.android.internal.org.bouncycastle.asn1.x509.Time;
+import com.android.internal.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import com.android.internal.org.bouncycastle.jce.X509Principal;
+import com.android.internal.org.bouncycastle.jce.provider.X509CertificateObject;
+import com.android.internal.org.bouncycastle.x509.X509V3CertificateGenerator;
import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index b1b6306..16bf546 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -23,7 +23,6 @@
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
-import android.sysprop.Keystore2Properties;
import java.io.IOException;
import java.security.KeyFactory;
@@ -117,8 +116,6 @@
putSecretKeyFactoryImpl("HmacSHA512");
}
- private static boolean sKeystore2Enabled;
-
/**
* This function indicates whether or not Keystore 2.0 is enabled. Some parts of the
* Keystore SPI must behave subtly differently when Keystore 2.0 is enabled. However,
@@ -133,10 +130,9 @@
* @hide
*/
public static boolean isKeystore2Enabled() {
- return sKeystore2Enabled;
+ return android.security.keystore2.AndroidKeyStoreProvider.isInstalled();
}
-
/**
* Installs a new instance of this provider (and the
* {@link AndroidKeyStoreBCWorkaroundProvider}).
@@ -164,11 +160,6 @@
// priority.
Security.addProvider(workaroundProvider);
}
-
- // {@code install()} is run by zygote when this property is still accessible. We store its
- // value so that the Keystore SPI can act accordingly without having to access an internal
- // property.
- sKeystore2Enabled = Keystore2Properties.keystore2_enabled().orElse(false);
}
private void putSecretKeyFactoryImpl(String algorithm) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 3694d63..d2678c7 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -215,7 +215,8 @@
// Keystore 1.0 does not tell us the exact security level of the key
// so we report an unknown but secure security level.
insideSecureHardware ? KeyProperties.SECURITY_LEVEL_UNKNOWN_SECURE
- : KeyProperties.SECURITY_LEVEL_SOFTWARE);
+ : KeyProperties.SECURITY_LEVEL_SOFTWARE,
+ KeyProperties.UNRESTRICTED_USAGE_COUNT);
}
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index e9aac7d..c2a7b2e 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -274,6 +274,7 @@
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
private final boolean mCriticalToDeviceEncryption;
+ private final int mMaxUsageCount;
/*
* ***NOTE***: All new fields MUST also be added to the following:
* ParcelableKeyGenParameterSpec class.
@@ -313,7 +314,8 @@
boolean isStrongBoxBacked,
boolean userConfirmationRequired,
boolean unlockedDeviceRequired,
- boolean criticalToDeviceEncryption) {
+ boolean criticalToDeviceEncryption,
+ int maxUsageCount) {
if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
}
@@ -366,6 +368,7 @@
mUserConfirmationRequired = userConfirmationRequired;
mUnlockedDeviceRequired = unlockedDeviceRequired;
mCriticalToDeviceEncryption = criticalToDeviceEncryption;
+ mMaxUsageCount = maxUsageCount;
}
/**
@@ -782,7 +785,7 @@
}
/**
- * Return whether this key is critical to the device encryption flow.
+ * Returns whether this key is critical to the device encryption flow.
*
* @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
* @hide
@@ -792,6 +795,17 @@
}
/**
+ * Returns the maximum number of times the limited use key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there’s no restriction on the number of
+ * times the key can be used.
+ *
+ * @see Builder#setMaxUsageCount(int)
+ */
+ public int getMaxUsageCount() {
+ return mMaxUsageCount;
+ }
+
+ /**
* Builder of {@link KeyGenParameterSpec} instances.
*/
public final static class Builder {
@@ -827,6 +841,7 @@
private boolean mUserConfirmationRequired;
private boolean mUnlockedDeviceRequired = false;
private boolean mCriticalToDeviceEncryption = false;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
/**
* Creates a new instance of the {@code Builder}.
@@ -894,6 +909,7 @@
mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired();
mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired();
mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption();
+ mMaxUsageCount = sourceSpec.getMaxUsageCount();
}
/**
@@ -1553,6 +1569,47 @@
}
/**
+ * Sets the maximum number of times the key is allowed to be used. After every use of the
+ * key, the use counter will decrease. This authorization applies only to secret key and
+ * private key operations. Public key operations are not restricted. For example, after
+ * successfully encrypting and decrypting data using methods such as
+ * {@link Cipher#doFinal()}, the use counter of the secret key will decrease. After
+ * successfully signing data using methods such as {@link Signature#sign()}, the use
+ * counter of the private key will decrease.
+ *
+ * When the use counter is depleted, the key will be marked for deletion by Android
+ * Keystore and any subsequent attempt to use the key will throw
+ * {@link KeyPermanentlyInvalidatedException}. There is no key to be loaded from the
+ * Android Keystore once the exhausted key is permanently deleted, as if the key never
+ * existed before.
+ *
+ * <p>By default, there is no restriction on the usage of key.
+ *
+ * <p>Some secure hardware may not support this feature at all, in which case it will
+ * be enforced in software, some secure hardware may support it but only with
+ * maxUsageCount = 1, and some secure hardware may support it with larger value
+ * of maxUsageCount.
+ *
+ * <p>The PackageManger feature flags:
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_SINGLE_USE_KEY} and
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_LIMITED_USE_KEY} can be used
+ * to check whether the secure hardware cannot enforce this feature, can only enforce it
+ * with maxUsageCount = 1, or can enforce it with larger value of maxUsageCount.
+ *
+ * @param maxUsageCount maximum number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there is no restriction on the
+ * usage.
+ */
+ @NonNull
+ public Builder setMaxUsageCount(int maxUsageCount) {
+ if (maxUsageCount == KeyProperties.UNRESTRICTED_USAGE_COUNT || maxUsageCount > 0) {
+ mMaxUsageCount = maxUsageCount;
+ return this;
+ }
+ throw new IllegalArgumentException("maxUsageCount is not valid");
+ }
+
+ /**
* Builds an instance of {@code KeyGenParameterSpec}.
*/
@NonNull
@@ -1587,7 +1644,8 @@
mIsStrongBoxBacked,
mUserConfirmationRequired,
mUnlockedDeviceRequired,
- mCriticalToDeviceEncryption);
+ mCriticalToDeviceEncryption,
+ mMaxUsageCount);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 7158d0c..f50efd2 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -85,6 +85,7 @@
private final boolean mInvalidatedByBiometricEnrollment;
private final boolean mUserConfirmationRequired;
private final @KeyProperties.SecurityLevelEnum int mSecurityLevel;
+ private final int mRemainingUsageCount;
/**
* @hide
@@ -109,7 +110,8 @@
boolean trustedUserPresenceRequired,
boolean invalidatedByBiometricEnrollment,
boolean userConfirmationRequired,
- @KeyProperties.SecurityLevelEnum int securityLevel) {
+ @KeyProperties.SecurityLevelEnum int securityLevel,
+ int remainingUsageCount) {
mKeystoreAlias = keystoreKeyAlias;
mInsideSecureHardware = insideSecureHardware;
mOrigin = origin;
@@ -134,6 +136,7 @@
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mUserConfirmationRequired = userConfirmationRequired;
mSecurityLevel = securityLevel;
+ mRemainingUsageCount = remainingUsageCount;
}
/**
@@ -374,4 +377,15 @@
public @KeyProperties.SecurityLevelEnum int getSecurityLevel() {
return mSecurityLevel;
}
+
+ /**
+ * Returns the remaining number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there's no restriction on the number of
+ * times the key can be used. Note that this gives a best effort count and need not be
+ * accurate (as there might be usages happening in parallel and the count maintained here need
+ * not be in sync with the usage).
+ */
+ public int getRemainingUsageCount() {
+ return mRemainingUsageCount;
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 014d688..fa4f8b1 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -907,4 +907,9 @@
+ uid);
}
}
+
+ /**
+ * This value indicates that there is no restriction on the number of times the key can be used.
+ */
+ public static final int UNRESTRICTED_USAGE_COUNT = -1;
}
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index e1e404d..aaa3715 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -235,6 +235,7 @@
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
private final boolean mIsStrongBoxBacked;
+ private final int mMaxUsageCount;
private KeyProtection(
Date keyValidityStart,
@@ -256,7 +257,8 @@
boolean criticalToDeviceEncryption,
boolean userConfirmationRequired,
boolean unlockedDeviceRequired,
- boolean isStrongBoxBacked) {
+ boolean isStrongBoxBacked,
+ int maxUsageCount) {
mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -279,6 +281,7 @@
mUserConfirmationRequired = userConfirmationRequired;
mUnlockedDeviceRequired = unlockedDeviceRequired;
mIsStrongBoxBacked = isStrongBoxBacked;
+ mMaxUsageCount = maxUsageCount;
}
/**
@@ -548,6 +551,17 @@
}
/**
+ * Returns the maximum number of times the limited use key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there’s no restriction on the number of
+ * times the key can be used.
+ *
+ * @see Builder#setMaxUsageCount(int)
+ */
+ public int getMaxUsageCount() {
+ return mMaxUsageCount;
+ }
+
+ /**
* Builder of {@link KeyProtection} instances.
*/
public final static class Builder {
@@ -574,6 +588,7 @@
private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
private boolean mCriticalToDeviceEncryption = false;
private boolean mIsStrongBoxBacked = false;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
/**
* Creates a new instance of the {@code Builder}.
@@ -1039,6 +1054,47 @@
}
/**
+ * Sets the maximum number of times the key is allowed to be used. After every use of the
+ * key, the use counter will decrease. This authorization applies only to secret key and
+ * private key operations. Public key operations are not restricted. For example, after
+ * successfully encrypting and decrypting data using methods such as
+ * {@link Cipher#doFinal()}, the use counter of the secret key will decrease. After
+ * successfully signing data using methods such as {@link Signature#sign()}, the use
+ * counter of the private key will decrease.
+ *
+ * When the use counter is depleted, the key will be marked for deletion by Android
+ * Keystore and any subsequent attempt to use the key will throw
+ * {@link KeyPermanentlyInvalidatedException}. There is no key to be loaded from the
+ * Android Keystore once the exhausted key is permanently deleted, as if the key never
+ * existed before.
+ *
+ * <p>By default, there is no restriction on the usage of key.
+ *
+ * <p>Some secure hardware may not support this feature at all, in which case it will
+ * be enforced in software, some secure hardware may support it but only with
+ * maxUsageCount = 1, and some secure hardware may support it with larger value
+ * of maxUsageCount.
+ *
+ * <p>The PackageManger feature flags:
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_SINGLE_USE_KEY} and
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_LIMITED_USE_KEY} can be used
+ * to check whether the secure hardware cannot enforce this feature, can only enforce it
+ * with maxUsageCount = 1, or can enforce it with larger value of maxUsageCount.
+ *
+ * @param maxUsageCount maximum number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there is no restriction on the
+ * usage.
+ */
+ @NonNull
+ public Builder setMaxUsageCount(int maxUsageCount) {
+ if (maxUsageCount == KeyProperties.UNRESTRICTED_USAGE_COUNT || maxUsageCount > 0) {
+ mMaxUsageCount = maxUsageCount;
+ return this;
+ }
+ throw new IllegalArgumentException("maxUsageCount is not valid");
+ }
+
+ /**
* Builds an instance of {@link KeyProtection}.
*
* @throws IllegalArgumentException if a required field is missing
@@ -1065,7 +1121,8 @@
mCriticalToDeviceEncryption,
mUserConfirmationRequired,
mUnlockedDeviceRequired,
- mIsStrongBoxBacked);
+ mIsStrongBoxBacked,
+ mMaxUsageCount);
}
}
}
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 69c15cc..8163472 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -108,6 +108,7 @@
out.writeBoolean(mSpec.isUserConfirmationRequired());
out.writeBoolean(mSpec.isUnlockedDeviceRequired());
out.writeBoolean(mSpec.isCriticalToDeviceEncryption());
+ out.writeInt(mSpec.getMaxUsageCount());
}
private static Date readDateOrNull(Parcel in) {
@@ -166,6 +167,7 @@
final boolean userConfirmationRequired = in.readBoolean();
final boolean unlockedDeviceRequired = in.readBoolean();
final boolean criticalToDeviceEncryption = in.readBoolean();
+ final int maxUsageCount = in.readInt();
// The KeyGenParameterSpec is intentionally not constructed using a Builder here:
// The intention is for this class to break if new parameters are added to the
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
@@ -199,7 +201,8 @@
isStrongBoxBacked,
userConfirmationRequired,
unlockedDeviceRequired,
- criticalToDeviceEncryption);
+ criticalToDeviceEncryption,
+ maxUsageCount);
}
public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index 233f352..1575bb4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -366,6 +366,13 @@
));
}
+ if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ params.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ spec.getMaxUsageCount()
+ ));
+ }
+
byte[] additionalEntropy =
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
mRng, (mKeySizeBits + 7) / 8);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index df0e146..6a92980 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -586,6 +586,13 @@
));
}
+ if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ params.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ mSpec.getMaxUsageCount()
+ ));
+ }
+
addAlgorithmSpecificParameters(params);
if (mSpec.isUniqueIdIncluded()) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 164bc86..75ac61a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -363,6 +363,11 @@
}
}
+ if (response.iSecurityLevel == null) {
+ // This seems to be a pure certificate entry, nothing to return here.
+ return null;
+ }
+
Integer keymasterAlgorithm = null;
// We just need one digest for the algorithm name
int keymasterDigest = -1;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 74503e1..fe05989 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -95,6 +95,7 @@
boolean userAuthenticationValidWhileOnBody = false;
boolean trustedUserPresenceRequired = false;
boolean trustedUserConfirmationRequired = false;
+ int remainingUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
try {
for (Authorization a : key.getAuthorizations()) {
switch (a.keyParameter.tag) {
@@ -195,6 +196,16 @@
trustedUserConfirmationRequired =
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
break;
+ case KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT:
+ long remainingUsageCountUnsigned =
+ KeyStore2ParameterUtils.getUnsignedInt(a);
+ if (remainingUsageCountUnsigned > Integer.MAX_VALUE) {
+ throw new ProviderException(
+ "Usage count of limited use key too long: "
+ + remainingUsageCountUnsigned);
+ }
+ remainingUsageCount = (int) remainingUsageCountUnsigned;
+ break;
}
}
} catch (IllegalArgumentException e) {
@@ -247,7 +258,8 @@
trustedUserPresenceRequired,
invalidatedByBiometricEnrollment,
trustedUserConfirmationRequired,
- securityLevel);
+ securityLevel,
+ remainingUsageCount);
}
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 5e7f648..8c8acc4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -490,7 +490,7 @@
int[] keymasterEncryptionPaddings =
KeyProperties.EncryptionPadding.allToKeymaster(
spec.getEncryptionPaddings());
- if (((spec.getPurposes() & KeyProperties.PURPOSE_DECRYPT) != 0)
+ if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (spec.isRandomizedEncryptionRequired())) {
for (int keymasterPadding : keymasterEncryptionPaddings) {
if (!KeymasterUtils
@@ -535,6 +535,12 @@
spec.getKeyValidityForConsumptionEnd()
));
}
+ if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ spec.getMaxUsageCount()
+ ));
+ }
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
@@ -772,6 +778,12 @@
params.getKeyValidityForConsumptionEnd()
));
}
+ if (params.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ params.getMaxUsageCount()
+ ));
+ }
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
diff --git a/packages/SystemUI/res/drawable/btn_restart.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/btn_restart.xml
rename to libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 2e0a5e0..93a6e7b 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -36,8 +36,8 @@
android:layout_height="match_parent">
<ImageButton
android:id="@+id/expand_button"
- android:layout_width="60dp"
- android:layout_height="60dp"
+ android:layout_width="@dimen/pip_expand_action_size"
+ android:layout_height="@dimen/pip_expand_action_size"
android:layout_gravity="center"
android:contentDescription="@string/pip_phone_expand"
android:padding="10dp"
@@ -97,4 +97,14 @@
android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" />
-</FrameLayout>
+
+ <!-- invisible layer to trap the focus, b/169372603 -->
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ android:defaultFocusHighlightEnabled="false"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:focusedByDefault="true" />
+t </FrameLayout>
diff --git a/packages/SystemUI/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
similarity index 100%
rename from packages/SystemUI/res/layout/size_compat_mode_hint.xml
rename to libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index e25a05c..034e65c 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -26,6 +26,9 @@
<!-- The height of the PiP actions container in which the actions are vertically centered. -->
<dimen name="pip_action_size">48dp</dimen>
+ <!-- The width and height of the PiP expand action. -->
+ <dimen name="pip_expand_action_size">60dp</dimen>
+
<!-- The padding between actions in the PiP in landscape Note that the PiP does not reflect
the configuration of the device, so we can't use -land resources. -->
<dimen name="pip_between_action_padding_land">8dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 30ef72c..b1425e4 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -146,4 +146,10 @@
<!-- Content description to tell the user a bubble has been dismissed. -->
<string name="accessibility_bubble_dismissed">Bubble dismissed.</string>
+
+ <!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
+ <string name="restart_button_description">Tap to restart this app and go full screen.</string>
+
+ <!-- Generic "got it" acceptance of dialog or cling [CHAR LIMIT=NONE] -->
+ <string name="got_it">Got it</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index 4b3fc81..afe523a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -106,5 +106,4 @@
public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
-
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 52648d9..fe97e24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -16,7 +16,7 @@
package com.android.wm.shell;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.common.ShellExecutor;
@@ -155,7 +155,7 @@
}
final int taskId = new Integer(args[2]);
final int sideStagePosition = args.length > 3
- ? new Integer(args[3]) : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ ? new Integer(args[3]) : STAGE_POSITION_BOTTOM_OR_RIGHT;
mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index c9b38d0..a570c0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -44,6 +44,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
import java.io.PrintWriter;
@@ -102,18 +103,31 @@
private final Object mLock = new Object();
private final StartingSurfaceDrawer mStartingSurfaceDrawer;
+ /**
+ * In charge of showing size compat UI. Can be {@code null} if device doesn't support size
+ * compat.
+ */
+ @Nullable
+ private final SizeCompatUI mSizeCompatUI;
+
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
- this(null, mainExecutor, context);
+ this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */);
+ }
+
+ public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
+ SizeCompatUI sizeCompatUI) {
+ this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
- Context context) {
+ Context context, @Nullable SizeCompatUI sizeCompatUI) {
super(taskOrganizerController, mainExecutor);
// TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled
// by a controller, that class should be create while porting
// ActivityRecord#addStartingWindow to WMShell.
mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, mainExecutor);
+ mSizeCompatUI = sizeCompatUI;
}
@Override
@@ -255,6 +269,7 @@
if (listener != null) {
listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
+ notifySizeCompatUI(info.getTaskInfo(), listener);
}
@Override
@@ -270,6 +285,10 @@
if (!updated && newListener != null) {
newListener.onTaskInfoChanged(taskInfo);
}
+ if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
+ // Notify the size compat UI if the listener or task info changed.
+ notifySizeCompatUI(taskInfo, newListener);
+ }
}
}
@@ -294,6 +313,8 @@
if (listener != null) {
listener.onTaskVanished(taskInfo);
}
+ // Pass null for listener to remove the size compat UI on this task if there is any.
+ notifySizeCompatUI(taskInfo, null /* taskListener */);
}
}
@@ -320,6 +341,34 @@
return true;
}
+ /**
+ * Notifies {@link SizeCompatUI} about the size compat info changed on the give Task to update
+ * the UI accordingly.
+ *
+ * @param taskInfo the new Task info
+ * @param taskListener listener to handle the Task Surface placement. {@code null} if task is
+ * vanished.
+ */
+ private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
+ if (mSizeCompatUI == null) {
+ return;
+ }
+
+ // The task is vanished, notify to remove size compat UI on this Task if there is any.
+ if (taskListener == null) {
+ mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+ null /* taskConfig */, null /* sizeCompatActivity*/,
+ null /* taskListener */);
+ return;
+ }
+
+ mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+ taskInfo.configuration.windowConfiguration.getBounds(),
+ // null if the top activity not in size compat.
+ taskInfo.topActivityInSizeCompat ? taskInfo.topActivityToken : null,
+ taskListener);
+ }
+
private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
return getTaskListener(runningTaskInfo, false /*removeLaunchCookieIfNeeded*/);
}
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 122f917..ffeabd8 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
@@ -49,6 +49,7 @@
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* Encapsulates the data and UI elements of a bubble.
@@ -58,6 +59,7 @@
private static final String TAG = "Bubble";
private final String mKey;
+ private final Executor mMainExecutor;
private long mLastUpdated;
private long mLastAccessed;
@@ -156,7 +158,8 @@
* Note: Currently this is only being used when the bubble is persisted to disk.
*/
Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
- final int desiredHeight, final int desiredHeightResId, @Nullable final String title) {
+ final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
+ Executor mainExecutor) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
mMetadataShortcutId = shortcutInfo.getId();
@@ -170,20 +173,25 @@
mDesiredHeightResId = desiredHeightResId;
mTitle = title;
mShowBubbleUpdateDot = false;
+ mMainExecutor = mainExecutor;
}
@VisibleForTesting(visibility = PRIVATE)
Bubble(@NonNull final BubbleEntry entry,
@Nullable final Bubbles.NotificationSuppressionChangedListener listener,
- final Bubbles.PendingIntentCanceledListener intentCancelListener) {
+ final Bubbles.PendingIntentCanceledListener intentCancelListener,
+ Executor mainExecutor) {
mKey = entry.getKey();
mSuppressionListener = listener;
mIntentCancelListener = intent -> {
if (mIntent != null) {
mIntent.unregisterCancelListener(mIntentCancelListener);
}
- intentCancelListener.onPendingIntentCanceled(this);
+ mainExecutor.execute(() -> {
+ intentCancelListener.onPendingIntentCanceled(this);
+ });
};
+ mMainExecutor = mainExecutor;
setEntry(entry);
}
@@ -329,7 +337,8 @@
stackView,
iconFactory,
skipInflation,
- callback);
+ callback,
+ mMainExecutor);
if (mInflateSynchronously) {
mInflationTask.onPostExecute(mInflationTask.doInBackground());
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 9419b9c..7538c8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -28,6 +28,16 @@
import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_LEFT;
import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_NONE;
import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_RIGHT;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_AGED;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_BLOCKED;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_GROUP_CANCELLED;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_INVALID_INTENT;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NOTIF_CANCEL;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NO_BUBBLE_UP;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NO_LONGER_BUBBLE;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_PACKAGE_REMOVED;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -45,6 +55,8 @@
import android.graphics.PointF;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -53,6 +65,7 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseSetArray;
import android.view.View;
import android.view.ViewGroup;
@@ -75,6 +88,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.IntConsumer;
/**
@@ -83,7 +99,7 @@
*
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
-public class BubbleController implements Bubbles {
+public class BubbleController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
@@ -101,7 +117,8 @@
public static final String BOTTOM_POSITION = "Bottom";
private final Context mContext;
- private BubbleExpandListener mExpandListener;
+ private final BubblesImpl mImpl = new BubblesImpl();
+ private Bubbles.BubbleExpandListener mExpandListener;
@Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
private final FloatingContentCoordinator mFloatingContentCoordinator;
private final BubbleDataRepository mDataRepository;
@@ -111,7 +128,7 @@
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
private BubblePositioner mBubblePositioner;
- private SysuiProxy mSysuiProxy;
+ private Bubbles.SysuiProxy mSysuiProxy;
// Tracks the id of the current (foreground) user.
private int mCurrentUserId;
@@ -177,7 +194,7 @@
/**
* Injected constructor.
*/
- public static BubbleController create(Context context,
+ public static Bubbles create(Context context,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
@Nullable IStatusBarService statusBarService,
@@ -186,14 +203,15 @@
LauncherApps launcherApps,
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor,
+ Handler mainHandler) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
BubblePositioner positioner = new BubblePositioner(context, windowManager);
- BubbleData data = new BubbleData(context, logger, positioner);
+ BubbleData data = new BubbleData(context, logger, positioner, mainExecutor);
return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
- new BubbleDataRepository(context, launcherApps),
+ new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- logger, organizer, positioner, mainExecutor);
+ logger, organizer, positioner, mainExecutor, mainHandler).mImpl;
}
/**
@@ -212,7 +230,8 @@
BubbleLogger bubbleLogger,
ShellTaskOrganizer organizer,
BubblePositioner positioner,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor,
+ Handler mainHandler) {
mContext = context;
mFloatingContentCoordinator = floatingContentCoordinator;
mDataRepository = dataRepository;
@@ -299,7 +318,12 @@
mBubbleData.removeBubblesWithInvalidShortcuts(
packageName, validShortcuts, DISMISS_SHORTCUT_REMOVED);
}
- });
+ }, mainHandler);
+ }
+
+ @VisibleForTesting
+ public Bubbles getImpl() {
+ return mImpl;
}
/**
@@ -313,8 +337,7 @@
}
}
- @Override
- public void openBubbleOverflow() {
+ private void openBubbleOverflow() {
ensureStackViewCreated();
mBubbleData.setShowingOverflow(true);
mBubbleData.setSelectedBubble(mBubbleData.getOverflow());
@@ -322,8 +345,7 @@
}
/** Called when any taskbar state changes (e.g. visibility, position, sizes). */
- @Override
- public void onTaskbarChanged(Bundle b) {
+ private void onTaskbarChanged(Bundle b) {
if (b == null) {
return;
}
@@ -371,8 +393,7 @@
* Called when the status bar has become visible or invisible (either permanently or
* temporarily).
*/
- @Override
- public void onStatusBarVisibilityChanged(boolean visible) {
+ private void onStatusBarVisibilityChanged(boolean visible) {
if (mStackView != null) {
// Hide the stack temporarily if the status bar has been made invisible, and the stack
// is collapsed. An expanded stack should remain visible until collapsed.
@@ -380,15 +401,13 @@
}
}
- @Override
- public void onZenStateChanged() {
+ private void onZenStateChanged() {
for (Bubble b : mBubbleData.getBubbles()) {
b.setShowDot(b.showInShade());
}
}
- @Override
- public void onStatusBarStateChanged(boolean isShade) {
+ private void onStatusBarStateChanged(boolean isShade) {
mIsStatusBarShade = isShade;
if (!mIsStatusBarShade) {
collapseStack();
@@ -402,8 +421,7 @@
updateStack();
}
- @Override
- public void onUserChanged(int newUserId) {
+ private void onUserChanged(int newUserId) {
saveBubbles(mCurrentUserId);
mBubbleData.dismissAll(DISMISS_USER_CHANGED);
restoreBubbles(newUserId);
@@ -442,7 +460,7 @@
return mBubblePositioner;
}
- SysuiProxy getSysuiProxy() {
+ Bubbles.SysuiProxy getSysuiProxy() {
return mSysuiProxy;
}
@@ -453,7 +471,8 @@
private void ensureStackViewCreated() {
if (mStackView == null) {
mStackView = new BubbleStackView(
- mContext, this, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator);
+ mContext, this, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
+ mMainExecutor);
mStackView.onOrientationChanged();
if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener);
@@ -576,8 +595,7 @@
mSavedBubbleKeysPerUser.remove(mCurrentUserId);
}
- @Override
- public void updateForThemeChanges() {
+ private void updateForThemeChanges() {
if (mStackView != null) {
mStackView.onThemeChanged();
}
@@ -593,8 +611,7 @@
}
}
- @Override
- public void onConfigChanged(Configuration newConfig) {
+ private void onConfigChanged(Configuration newConfig) {
if (mBubblePositioner != null) {
// This doesn't trigger any changes, always update it
mBubblePositioner.update(newConfig.orientation);
@@ -620,18 +637,19 @@
}
}
- @Override
- public void setBubbleScrim(View view) {
+ private void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback) {
mBubbleScrim = view;
+ callback.accept(mMainExecutor, mMainExecutor.executeBlockingForResult(() -> {
+ return Looper.myLooper();
+ }, Looper.class));
}
- @Override
- public void setSysuiProxy(SysuiProxy proxy) {
+ private void setSysuiProxy(Bubbles.SysuiProxy proxy) {
mSysuiProxy = proxy;
}
- @Override
- public void setExpandListener(BubbleExpandListener listener) {
+ @VisibleForTesting
+ public void setExpandListener(Bubbles.BubbleExpandListener listener) {
mExpandListener = ((isExpanding, key) -> {
if (listener != null) {
listener.onBubbleExpandChanged(isExpanding, key);
@@ -654,17 +672,17 @@
return mBubbleData.hasBubbles() || mBubbleData.isShowingOverflow();
}
- @Override
+ @VisibleForTesting
public boolean isStackExpanded() {
return mBubbleData.isExpanded();
}
- @Override
+ @VisibleForTesting
public void collapseStack() {
mBubbleData.setExpanded(false /* expanded */);
}
- @Override
+ @VisibleForTesting
public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
&& !mBubbleData.getAnyBubbleWithkey(key).showInShade());
@@ -674,23 +692,19 @@
return (isSummary && isSuppressedSummary) || isSuppressedBubble;
}
- @Override
- public boolean isSummarySuppressed(String groupKey) {
- return mBubbleData.isSummarySuppressed(groupKey);
+ private void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
+ Executor callbackExecutor) {
+ if (mBubbleData.isSummarySuppressed(groupKey)) {
+ mBubbleData.removeSuppressedSummary(groupKey);
+ if (callback != null) {
+ callbackExecutor.execute(() -> {
+ callback.accept(mBubbleData.getSummaryKey(groupKey));
+ });
+ }
+ }
}
- @Override
- public void removeSuppressedSummary(String groupKey) {
- mBubbleData.removeSuppressedSummary(groupKey);
- }
-
- @Override
- public String getSummaryKey(String groupKey) {
- return mBubbleData.getSummaryKey(groupKey);
- }
-
- @Override
- public boolean isBubbleExpanded(String key) {
+ private boolean isBubbleExpanded(String key) {
return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
&& mBubbleData.getSelectedBubble().getKey().equals(key);
}
@@ -704,7 +718,7 @@
setIsBubble(bubble, true /* isBubble */);
}
- @Override
+ @VisibleForTesting
public void expandStackAndSelectBubble(BubbleEntry entry) {
if (mIsStatusBarShade) {
mNotifEntryToExpandOnShadeUnlock = null;
@@ -809,15 +823,13 @@
}
}
- @Override
- public void onEntryAdded(BubbleEntry entry) {
+ private void onEntryAdded(BubbleEntry entry) {
if (canLaunchInActivityView(mContext, entry)) {
updateBubble(entry);
}
}
- @Override
- public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+ private void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
// shouldBubbleUp checks canBubble & for bubble metadata
boolean shouldBubble = shouldBubbleUp && canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -828,8 +840,7 @@
}
}
- @Override
- public void onEntryRemoved(BubbleEntry entry) {
+ private void onEntryRemoved(BubbleEntry entry) {
if (isSummaryOfBubbles(entry)) {
final String groupKey = entry.getStatusBarNotification().getGroupKey();
mBubbleData.removeSuppressedSummary(groupKey);
@@ -844,8 +855,7 @@
}
}
- @Override
- public void onRankingUpdated(RankingMap rankingMap) {
+ private void onRankingUpdated(RankingMap rankingMap) {
if (mTmpRanking == null) {
mTmpRanking = new NotificationListenerService.Ranking();
}
@@ -882,6 +892,8 @@
return bubbleChildren;
}
for (Bubble bubble : mBubbleData.getActiveBubbles()) {
+ // TODO(178620678): Prevent calling into SysUI since this can be a part of a blocking
+ // call from SysUI to Shell
final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey());
if (entry != null && groupKey.equals(entry.getStatusBarNotification().getGroupKey())) {
bubbleChildren.add(bubble);
@@ -951,7 +963,7 @@
ArrayList<Bubble> bubblesToBeRemovedFromRepository = new ArrayList<>();
for (Pair<Bubble, Integer> removed : removedBubbles) {
final Bubble bubble = removed.first;
- @DismissReason final int reason = removed.second;
+ @Bubbles.DismissReason final int reason = removed.second;
if (mStackView != null) {
mStackView.removeBubble(bubble);
@@ -1029,8 +1041,7 @@
}
};
- @Override
- public boolean handleDismissalInterception(BubbleEntry entry,
+ private boolean handleDismissalInterception(BubbleEntry entry,
@Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
if (isSummaryOfBubbles(entry)) {
handleSummaryDismissalInterception(entry, children, removeCallback);
@@ -1137,8 +1148,7 @@
/**
* Description of current bubble state.
*/
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ private void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("BubbleController state:");
mBubbleData.dump(fd, pw, args);
pw.println();
@@ -1216,4 +1226,175 @@
}
}
}
+
+ private class BubblesImpl implements Bubbles {
+ @Override
+ public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return BubbleController.this.isBubbleNotificationSuppressedFromShade(key, groupKey);
+ }, Boolean.class);
+ }
+
+ @Override
+ public boolean isBubbleExpanded(String key) {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return BubbleController.this.isBubbleExpanded(key);
+ }, Boolean.class);
+ }
+
+ @Override
+ public boolean isStackExpanded() {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return BubbleController.this.isStackExpanded();
+ }, Boolean.class);
+ }
+
+ @Override
+ public void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
+ Executor callbackExecutor) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.removeSuppressedSummaryIfNecessary(groupKey, callback,
+ callbackExecutor);
+ });
+ }
+
+ @Override
+ public void collapseStack() {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.collapseStack();
+ });
+ }
+
+ @Override
+ public void updateForThemeChanges() {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.updateForThemeChanges();
+ });
+ }
+
+ @Override
+ public void expandStackAndSelectBubble(BubbleEntry entry) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.expandStackAndSelectBubble(entry);
+ });
+ }
+
+ @Override
+ public void onTaskbarChanged(Bundle b) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onTaskbarChanged(b);
+ });
+ }
+
+ @Override
+ public void openBubbleOverflow() {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.openBubbleOverflow();
+ });
+ }
+
+ @Override
+ public boolean handleDismissalInterception(BubbleEntry entry,
+ @Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return BubbleController.this.handleDismissalInterception(entry, children,
+ removeCallback);
+ }, Boolean.class);
+ }
+
+ @Override
+ public void setSysuiProxy(SysuiProxy proxy) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.setSysuiProxy(proxy);
+ });
+ }
+
+ @Override
+ public void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.setBubbleScrim(view, callback);
+ });
+ }
+
+ @Override
+ public void setExpandListener(BubbleExpandListener listener) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.setExpandListener(listener);
+ });
+ }
+
+ @Override
+ public void onEntryAdded(BubbleEntry entry) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onEntryAdded(entry);
+ });
+ }
+
+ @Override
+ public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onEntryUpdated(entry, shouldBubbleUp);
+ });
+ }
+
+ @Override
+ public void onEntryRemoved(BubbleEntry entry) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onEntryRemoved(entry);
+ });
+ }
+
+ @Override
+ public void onRankingUpdated(RankingMap rankingMap) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onRankingUpdated(rankingMap);
+ });
+ }
+
+ @Override
+ public void onStatusBarVisibilityChanged(boolean visible) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onStatusBarVisibilityChanged(visible);
+ });
+ }
+
+ @Override
+ public void onZenStateChanged() {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onZenStateChanged();
+ });
+ }
+
+ @Override
+ public void onStatusBarStateChanged(boolean isShade) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onStatusBarStateChanged(isShade);
+ });
+ }
+
+ @Override
+ public void onUserChanged(int newUserId) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onUserChanged(newUserId);
+ });
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onConfigChanged(newConfig);
+ });
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ try {
+ mMainExecutor.executeBlocking(() -> {
+ BubbleController.this.dump(fd, pw, args);
+ });
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Failed to dump BubbleController in 2s");
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index e24ff06..9d196ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -46,6 +46,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -117,6 +118,7 @@
private final Context mContext;
private final BubblePositioner mPositioner;
+ private final Executor mMainExecutor;
/** Bubbles that are actively in the stack. */
private final List<Bubble> mBubbles;
/** Bubbles that aged out to overflow. */
@@ -155,10 +157,12 @@
*/
private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
- public BubbleData(Context context, BubbleLogger bubbleLogger, BubblePositioner positioner) {
+ public BubbleData(Context context, BubbleLogger bubbleLogger, BubblePositioner positioner,
+ Executor mainExecutor) {
mContext = context;
mLogger = bubbleLogger;
mPositioner = positioner;
+ mMainExecutor = mainExecutor;
mOverflow = new BubbleOverflow(context, positioner);
mBubbles = new ArrayList<>();
mOverflowBubbles = new ArrayList<>();
@@ -264,7 +268,8 @@
bubbleToReturn = mPendingBubbles.get(key);
} else if (entry != null) {
// New bubble
- bubbleToReturn = new Bubble(entry, mSuppressionListener, mCancelledListener);
+ bubbleToReturn = new Bubble(entry, mSuppressionListener, mCancelledListener,
+ mMainExecutor);
} else {
// Persisted bubble being promoted
bubbleToReturn = persistedBubble;
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 fc565f1..3108b02 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
@@ -27,6 +27,8 @@
import com.android.wm.shell.bubbles.storage.BubbleEntity
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
@@ -34,12 +36,12 @@
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
-internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps) {
+internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps,
+ private val mainExecutor : ShellExecutor) {
private val volatileRepository = BubbleVolatileRepository(launcherApps)
private val persistentRepository = BubblePersistentRepository(context)
private val ioScope = CoroutineScope(Dispatchers.IO)
- private val uiScope = CoroutineScope(Dispatchers.Main)
private var job: Job? = null
/**
@@ -109,6 +111,8 @@
/**
* Load bubbles from disk.
+ * @param cb The callback to be run after the bubbles are loaded. This callback is always made
+ * on the main thread of the hosting process.
*/
@SuppressLint("WrongConstant")
fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch {
@@ -163,10 +167,11 @@
shortcutInfo,
entity.desiredHeight,
entity.desiredHeightResId,
- entity.title
+ entity.title,
+ mainExecutor
) }
}
- uiScope.launch { cb(bubbles) }
+ mainExecutor.execute { cb(bubbles) }
}
}
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 fac3686..af421fa 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
@@ -79,6 +79,7 @@
import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import java.io.FileDescriptor;
@@ -147,7 +148,7 @@
* Handler to use for all delayed animations - this way, we can easily cancel them before
* starting a new animation.
*/
- private final Handler mDelayedAnimationHandler = new Handler();
+ private final ShellExecutor mDelayedAnimationExecutor;
/**
* Interface to synchronize {@link View} state and the screen.
@@ -311,7 +312,7 @@
}
}
- private BubbleController.BubbleExpandListener mExpandListener;
+ private Bubbles.BubbleExpandListener mExpandListener;
/** Callback to run when we want to unbubble the given notification's conversation. */
private Consumer<String> mUnbubbleConversationCallback;
@@ -734,9 +735,11 @@
@SuppressLint("ClickableViewAccessibility")
public BubbleStackView(Context context, BubbleController bubbleController,
BubbleData data, @Nullable SurfaceSynchronizer synchronizer,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ ShellExecutor mainExecutor) {
super(context);
+ mDelayedAnimationExecutor = mainExecutor;
mBubbleController = bubbleController;
mBubbleData = data;
@@ -1366,7 +1369,7 @@
/**
* Sets the listener to notify when the bubble stack is expanded.
*/
- public void setExpandListener(BubbleController.BubbleExpandListener listener) {
+ public void setExpandListener(Bubbles.BubbleExpandListener listener) {
mExpandListener = listener;
}
@@ -1734,7 +1737,7 @@
mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
}
- mDelayedAnimationHandler.postDelayed(() -> {
+ mDelayedAnimationExecutor.executeDelayed(() -> {
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
.spring(AnimatableScaleMatrix.SCALE_X,
@@ -1791,10 +1794,12 @@
final long startDelay =
(long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f);
- mDelayedAnimationHandler.postDelayed(() -> mExpandedAnimationController.collapseBackToStack(
- mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
- /* collapseTo */,
- () -> mBubbleContainer.setActiveController(mStackAnimationController)), startDelay);
+ mDelayedAnimationExecutor.executeDelayed(() -> {
+ mExpandedAnimationController.collapseBackToStack(
+ mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
+ /* collapseTo */,
+ () -> mBubbleContainer.setActiveController(mStackAnimationController));
+ }, startDelay);
if (mTaskbarScrim.getVisibility() == VISIBLE) {
mTaskbarScrim.animate().alpha(0f).start();
@@ -1945,7 +1950,7 @@
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
- mDelayedAnimationHandler.postDelayed(() -> {
+ mDelayedAnimationExecutor.executeDelayed(() -> {
if (!mIsExpanded) {
mIsBubbleSwitchAnimating = false;
return;
@@ -1978,7 +1983,7 @@
* animating flags for those animations.
*/
private void cancelDelayedExpandCollapseSwitchAnimations() {
- mDelayedAnimationHandler.removeCallbacksAndMessages(null);
+ mDelayedAnimationExecutor.removeAllCallbacks();
mIsExpansionAnimating = false;
mIsBubbleSwitchAnimating = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 0e7e92d..c5a712e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -46,6 +46,7 @@
import java.lang.ref.WeakReference;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* Simple task to inflate views & load necessary info to display a bubble.
@@ -71,6 +72,7 @@
private BubbleIconFactory mIconFactory;
private boolean mSkipInflation;
private Callback mCallback;
+ private Executor mMainExecutor;
/**
* Creates a task to load information for the provided {@link Bubble}. Once all info
@@ -82,7 +84,8 @@
BubbleStackView stackView,
BubbleIconFactory factory,
boolean skipInflation,
- Callback c) {
+ Callback c,
+ Executor mainExecutor) {
mBubble = b;
mContext = new WeakReference<>(context);
mController = new WeakReference<>(controller);
@@ -90,6 +93,7 @@
mIconFactory = factory;
mSkipInflation = skipInflation;
mCallback = c;
+ mMainExecutor = mainExecutor;
}
@Override
@@ -103,10 +107,12 @@
if (isCancelled() || viewInfo == null) {
return;
}
- mBubble.setViewInfo(viewInfo);
- if (mCallback != null) {
- mCallback.onBubbleViewsReady(mBubble);
- }
+ mMainExecutor.execute(() -> {
+ mBubble.setViewInfo(viewInfo);
+ if (mCallback != null) {
+ mCallback.onBubbleViewsReady(mBubble);
+ }
+ });
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index fa5ac44..6102147 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -23,6 +23,7 @@
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.Looper;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.view.View;
@@ -37,6 +38,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.IntConsumer;
/**
@@ -86,13 +90,14 @@
/** @return {@code true} if stack of bubbles is expanded or not. */
boolean isStackExpanded();
- /** @return {@code true} if the summary for the provided group key is suppressed. */
- boolean isSummarySuppressed(String groupKey);
-
/**
- * Removes a group key indicating that summary for this group should no longer be suppressed.
+ * Removes a group key indicating that the summary for this group should no longer be
+ * suppressed.
+ *
+ * @param callback If removed, this callback will be called with the summary key of the group
*/
- void removeSuppressedSummary(String groupKey);
+ void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
+ Executor callbackExecutor);
/** Tell the stack of bubbles to collapse. */
void collapseStack();
@@ -134,19 +139,16 @@
boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children,
IntConsumer removeCallback);
- /**
- * Retrieves the notif entry key of the summary associated with the provided group key.
- *
- * @param groupKey the group to look up
- * @return the key for the notification that is the summary of this group.
- */
- String getSummaryKey(String groupKey);
-
/** Set the proxy to commnuicate with SysUi side components. */
void setSysuiProxy(SysuiProxy proxy);
- /** Set the scrim view for bubbles. */
- void setBubbleScrim(View view);
+ /**
+ * Set the scrim view for bubbles.
+ *
+ * @param callback The callback made with the executor and the executor's looper that the view
+ * will be running on.
+ **/
+ void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback);
/** Set a listener to be notified of bubble expand events. */
void setExpandListener(BubbleExpandListener listener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
index a1b0dbe..cf0cefe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
@@ -91,7 +91,6 @@
private var touchSlop: Int = -1
private var movedEnough = false
- private val handler = Handler(Looper.myLooper()!!)
private var performedLongClick = false
@Suppress("UNCHECKED_CAST")
@@ -115,7 +114,7 @@
viewPositionOnTouchDown.set(v.translationX, v.translationY)
performedLongClick = false
- handler.postDelayed({
+ v.handler.postDelayed({
if (v.isLongClickable) {
performedLongClick = v.performLongClick()
}
@@ -125,7 +124,7 @@
MotionEvent.ACTION_MOVE -> {
if (!movedEnough && hypot(dx, dy) > touchSlop && !performedLongClick) {
movedEnough = true
- handler.removeCallbacksAndMessages(null)
+ v.handler.removeCallbacksAndMessages(null)
}
if (movedEnough) {
@@ -141,7 +140,7 @@
} else if (!performedLongClick) {
v.performClick()
} else {
- handler.removeCallbacksAndMessages(null)
+ v.handler.removeCallbacksAndMessages(null)
}
velocityTracker.clear()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index e1f831e..73371e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -62,8 +62,8 @@
private static final String TAG = "Bubbs.StackCtrl";
- /** Values to use for animating bubbles in. */
- private static final float ANIMATE_IN_STIFFNESS = 1000f;
+ /** Value to use for animating bubbles in and springing stack after fling. */
+ private static final float STACK_SPRING_STIFFNESS = 700f;
/** Values to use for animating updated bubble to top of stack. */
private static final float NEW_BUBBLE_START_SCALE = 0.5f;
@@ -80,20 +80,15 @@
private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
new PhysicsAnimator.SpringConfig(
- ANIMATE_IN_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+ STACK_SPRING_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
/**
* Friction applied to fling animations. Since the stack must land on one of the sides of the
* screen, we want less friction horizontally so that the stack has a better chance of making it
* to the side without needing a spring.
*/
- private static final float FLING_FRICTION = 2.2f;
+ private static final float FLING_FRICTION = 1.9f;
- /**
- * Values to use for the stack spring animation used to spring the stack to its final position
- * after a fling.
- */
- private static final int SPRING_AFTER_FLING_STIFFNESS = 750;
private static final float SPRING_AFTER_FLING_DAMPING_RATIO = 0.85f;
/** Sentinel value for unset position value. */
@@ -216,7 +211,7 @@
@Override
public void moveToBounds(@NonNull Rect bounds) {
- springStack(bounds.left, bounds.top, SpringForce.STIFFNESS_LOW);
+ springStack(bounds.left, bounds.top, STACK_SPRING_STIFFNESS);
}
@NonNull
@@ -341,7 +336,7 @@
* flings.
*/
public void springStackAfterFling(float destinationX, float destinationY) {
- springStack(destinationX, destinationY, SPRING_AFTER_FLING_STIFFNESS);
+ springStack(destinationX, destinationY, STACK_SPRING_STIFFNESS);
}
/**
@@ -371,7 +366,7 @@
final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
final float stiffness = Settings.Secure.getFloat(contentResolver, "bubble_stiffness",
- SPRING_AFTER_FLING_STIFFNESS /* default */);
+ STACK_SPRING_STIFFNESS /* default */);
final float dampingRatio = Settings.Secure.getFloat(contentResolver, "bubble_damping",
SPRING_AFTER_FLING_DAMPING_RATIO);
final float friction = Settings.Secure.getFloat(contentResolver, "bubble_friction",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index a2cd683..b2ac61c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -159,6 +159,14 @@
}
}
+ private void dispatchVisibilityChanged(int displayId, boolean isShowing) {
+ synchronized (mPositionProcessors) {
+ for (ImePositionProcessor pp : mPositionProcessors) {
+ pp.onImeVisibilityChanged(displayId, isShowing);
+ }
+ }
+ }
+
/**
* Adds an {@link ImePositionProcessor} to be called during ime position updates.
*/
@@ -212,7 +220,7 @@
return;
}
- mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME);
+ updateImeVisibility(insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME));
final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME);
final Rect newFrame = newSource.getFrame();
@@ -371,7 +379,7 @@
seek = true;
}
mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
- mImeShowing = show;
+ updateImeVisibility(show);
mAnimation = ValueAnimator.ofFloat(startY, endY);
mAnimation.setDuration(
show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS);
@@ -455,6 +463,13 @@
}
}
+ private void updateImeVisibility(boolean isShowing) {
+ if (mImeShowing != isShowing) {
+ mImeShowing = isShowing;
+ dispatchVisibilityChanged(mDisplayId, isShowing);
+ }
+ }
+
@VisibleForTesting
@BinderThread
public class DisplayWindowInsetsControllerImpl
@@ -563,6 +578,15 @@
default void onImeEndPositioning(int displayId, boolean cancel,
SurfaceControl.Transaction t) {
}
+
+ /**
+ * Called when the IME visibility changed.
+ *
+ * @param isShowing {@code true} if the IME is shown.
+ */
+ default void onImeVisibilityChanged(int displayId, boolean isShowing) {
+
+ }
}
public IInputMethodManager getImms() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index b736fb0..6abc8f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -20,6 +20,7 @@
import android.os.SystemClock;
import android.os.Trace;
+import java.lang.reflect.Array;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -68,6 +69,26 @@
}
/**
+ * Convenience method to execute the blocking call with a default timeout and returns a value.
+ * Waits indefinitely for a typed result from a call.
+ */
+ default <T> T executeBlockingForResult(Supplier<T> runnable, Class clazz) {
+ final T[] result = (T[]) Array.newInstance(clazz, 1);
+ final CountDownLatch latch = new CountDownLatch(1);
+ execute(() -> {
+ result[0] = runnable.get();
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ return result[0];
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+
+
+ /**
* See {@link android.os.Handler#postDelayed(Runnable, long)}.
*/
void executeDelayed(Runnable runnable, long delayMillis);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 800150c..35dcdd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -34,8 +34,11 @@
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -84,8 +87,7 @@
private DragSession mSession;
public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
- this(context, ActivityTaskManager.getInstance(), splitScreen,
- new DefaultStarter(context, splitScreen));
+ this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context));
}
@VisibleForTesting
@@ -94,7 +96,7 @@
mContext = context;
mActivityTaskManager = activityTaskManager;
mSplitScreen = splitScreen;
- mStarter = starter;
+ mStarter = mSplitScreen != null ? mSplitScreen : starter;
}
/**
@@ -195,39 +197,23 @@
return;
}
- final ClipDescription description = data.getDescription();
- final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
- final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
- final Intent dragData = mSession.dragData;
final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
- final Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
- ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
- : new Bundle();
- if (target.type == TYPE_FULLSCREEN) {
- // Exit split stages if needed
- mStarter.exitSplitScreen();
- } else if (mSplitScreen != null) {
+ @SplitScreen.StageType int stage = STAGE_TYPE_UNDEFINED;
+ @SplitScreen.StagePosition int position = STAGE_POSITION_UNDEFINED;
+ if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
// Update launch options for the split side we are targeting.
- final int position = leftOrTop
- ? SIDE_STAGE_POSITION_TOP_OR_LEFT : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
if (!inSplitScreen) {
- // Update the side stage position to match where we want to launch.
- mSplitScreen.setSideStagePosition(position);
+ // Launch in the side stage if we are not in split-screen already.
+ stage = STAGE_TYPE_SIDE;
}
- mSplitScreen.updateActivityOptions(opts, position);
}
- if (isTask) {
- mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts);
- } else if (isShortcut) {
- mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME),
- dragData.getStringExtra(EXTRA_SHORTCUT_ID),
- opts, dragData.getParcelableExtra(EXTRA_USER));
- } else {
- mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
- }
+ final ClipDescription description = data.getDescription();
+ final Intent dragData = mSession.dragData;
+ mStarter.startClipDescription(description, dragData, stage, position);
}
/**
@@ -247,7 +233,6 @@
int runningTaskActType = ACTIVITY_TYPE_STANDARD;
boolean runningTaskIsResizeable;
boolean dragItemSupportsSplitscreen;
- boolean isPhone;
DragSession(Context context, ActivityTaskManager activityTaskManager,
DisplayLayout dispLayout, ClipData data) {
@@ -275,7 +260,6 @@
final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo();
dragItemSupportsSplitscreen = info == null
|| ActivityInfo.isResizeableMode(info.resizeMode);
- isPhone = mContext.getResources().getConfiguration().smallestScreenWidthDp < 600;
dragData = mInitialDragData.getItemAt(0).getIntent();
}
}
@@ -284,11 +268,33 @@
* Interface for actually committing the task launches.
*/
@VisibleForTesting
- interface Starter {
- void startTask(int taskId, Bundle activityOptions);
- void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
- UserHandle user);
- void startIntent(PendingIntent intent, Bundle activityOptions);
+ public interface Starter {
+ default void startClipDescription(ClipDescription description, Intent intent,
+ @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position) {
+ final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+ final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+ ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
+
+ if (isTask) {
+ final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+ startTask(taskId, stage, position, opts);
+ } else if (isShortcut) {
+ final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+ final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+ final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
+ startShortcut(packageName, id, stage, position, opts, user);
+ } else {
+ startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, opts);
+ }
+ }
+ void startTask(int taskId, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId,
+ @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+ @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
void exitSplitScreen();
}
@@ -299,39 +305,39 @@
*/
private static class DefaultStarter implements Starter {
private final Context mContext;
- private final SplitScreen mSplitScreen;
- public DefaultStarter(Context context, SplitScreen splitScreen) {
+ public DefaultStarter(Context context) {
mContext = context;
- mSplitScreen = splitScreen;
}
@Override
- public void startTask(int taskId, Bundle activityOptions) {
+ public void startTask(int taskId, int stage, int position,
+ @Nullable Bundle options) {
try {
- ActivityTaskManager.getService().startActivityFromRecents(taskId, activityOptions);
+ ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to launch task", e);
}
}
@Override
- public void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
- UserHandle user) {
+ public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ @Nullable Bundle options, UserHandle user) {
try {
LauncherApps launcherApps =
mContext.getSystemService(LauncherApps.class);
launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
- activityOptions, user);
+ options, user);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "Failed to launch shortcut", e);
}
}
@Override
- public void startIntent(PendingIntent intent, Bundle activityOptions) {
+ public void startIntent(PendingIntent intent, int stage, int position,
+ @Nullable Bundle options) {
try {
- intent.send(null, 0, null, null, null, null, activityOptions);
+ intent.send(null, 0, null, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
Slog.e(TAG, "Failed to launch activity", e);
}
@@ -339,14 +345,12 @@
@Override
public void enterSplitScreen(int taskId, boolean leftOrTop) {
- mSplitScreen.moveToSideStage(taskId,
- leftOrTop ? SIDE_STAGE_POSITION_TOP_OR_LEFT
- : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
+ throw new UnsupportedOperationException("enterSplitScreen not implemented by starter");
}
@Override
public void exitSplitScreen() {
- mSplitScreen.exitSplitScreen();
+ throw new UnsupportedOperationException("exitSplitScreen not implemented by starter");
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 1da72f8..04d1264 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -89,6 +89,7 @@
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
if (mAnimationController.isAnimatorsConsumed()) {
+ resetWindowsOffsetInternal(animator.getTransitionDirection());
finishOffset(animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -99,6 +100,7 @@
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
if (mAnimationController.isAnimatorsConsumed()) {
+ resetWindowsOffsetInternal(animator.getTransitionDirection());
finishOffset(animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -205,6 +207,16 @@
applyTransaction(wct);
}
+ private void resetWindowsOffsetInternal(
+ @OneHandedAnimationController.TransitionDirection int td) {
+ if (td == TRANSITION_DIRECTION_TRIGGER) {
+ return;
+ }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ resetWindowsOffset(wct);
+ applyTransaction(wct);
+ }
+
private void resetWindowsOffset(WindowContainerTransaction wct) {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 8bf1b46..a57eee8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -34,6 +34,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Size;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
@@ -210,6 +211,11 @@
}
}
+ @Nullable
+ Size getEstimatedMinMenuSize() {
+ return mPipMenuView == null ? null : mPipMenuView.getEstimatedMinMenuSize();
+ }
+
/**
* When other components requests the menu controller directly to show the menu, we must
* first fire off the request to the other listeners who will then propagate the call
@@ -224,13 +230,13 @@
* Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu
* upon PiP window transition is finished.
*/
- public void showMenuWithDelay(int menuState, Rect stackBounds, boolean allowMenuTimeout,
+ public void showMenuWithPossibleDelay(int menuState, Rect stackBounds, boolean allowMenuTimeout,
boolean willResizeMenu, boolean showResizeHandle) {
// hide all visible controls including close button and etc. first, this is to ensure
// menu is totally invisible during the transition to eliminate unpleasant artifacts
fadeOutMenu();
showMenuInternal(menuState, stackBounds, allowMenuTimeout, willResizeMenu,
- true /* withDelay */, showResizeHandle);
+ willResizeMenu /* withDelay=willResizeMenu here */, showResizeHandle);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 2e515ee..962c467 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -48,6 +48,7 @@
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
+import android.util.Size;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -76,9 +77,6 @@
private static final String TAG = "PipMenuView";
- private static final int MESSAGE_INVALID_TYPE = -1;
- public static final int MESSAGE_MENU_EXPANDED = 8;
-
private static final int INITIAL_DISMISS_DELAY = 3500;
private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
private static final long MENU_FADE_DURATION = 125;
@@ -86,8 +84,6 @@
private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
- private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
-
private static final float DISABLED_ACTION_ALPHA = 0.54f;
private static final boolean ENABLE_RESIZE_HANDLE = false;
@@ -235,7 +231,6 @@
&& (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
mAllowTouches = !disallowTouchesUntilAnimationEnd;
cancelDelayedHide();
- updateActionViews(stackBounds);
if (mMenuContainerAnimator != null) {
mMenuContainerAnimator.cancel();
}
@@ -284,6 +279,7 @@
setVisibility(VISIBLE);
mMenuContainerAnimator.start();
}
+ updateActionViews(stackBounds);
} else {
// If we are already visible, then just start the delayed dismiss and unregister any
// existing input consumers from the previous drag
@@ -370,6 +366,21 @@
}
}
+ /**
+ * @return Estimated minimum {@link Size} to hold the actions.
+ * See also {@link #updateActionViews(Rect)}
+ */
+ Size getEstimatedMinMenuSize() {
+ final int pipActionSize = getResources().getDimensionPixelSize(R.dimen.pip_action_size);
+ // the minimum width would be (2 * pipActionSize) since we have settings and dismiss button
+ // on the top action container.
+ final int width = Math.max(2, mActions.size()) * pipActionSize;
+ final int height = getResources().getDimensionPixelSize(R.dimen.pip_expand_action_size)
+ + getResources().getDimensionPixelSize(R.dimen.pip_action_padding)
+ + getResources().getDimensionPixelSize(R.dimen.pip_expand_container_edge_margin);
+ return new Size(width, height);
+ }
+
void setActions(Rect stackBounds, List<RemoteAction> actions) {
mActions.clear();
mActions.addAll(actions);
@@ -384,7 +395,7 @@
return true;
});
- if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
+ if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE || mMenuState == MENU_STATE_NONE) {
actionsContainer.setVisibility(View.INVISIBLE);
} else {
actionsContainer.setVisibility(View.VISIBLE);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 4df7cef..fd4ea61 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -27,7 +27,6 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Debug;
-import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.Choreographer;
@@ -62,6 +61,7 @@
private static final int SHRINK_STACK_FROM_MENU_DURATION = 250;
private static final int EXPAND_STACK_TO_MENU_DURATION = 250;
+ private static final int UNSTASH_DURATION = 250;
private static final int LEAVE_PIP_DURATION = 300;
private static final int SHIFT_DURATION = 300;
@@ -482,6 +482,13 @@
}
/**
+ * Animates the PiP from stashed state into un-stashed, popping it out from the edge.
+ */
+ void animateToUnStashedBounds(Rect unstashedBounds) {
+ resizeAndAnimatePipUnchecked(unstashedBounds, UNSTASH_DURATION);
+ }
+
+ /**
* Animates the PiP to offset it from the IME or shelf.
*/
@VisibleForTesting
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 2b8b53c..3cb3ae8 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
@@ -17,7 +17,9 @@
package com.android.wm.shell.pip.phone;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASH_MINIMUM_VELOCITY_THRESHOLD;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
@@ -30,8 +32,8 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.os.Handler;
import android.provider.DeviceConfig;
+import android.util.Log;
import android.util.Size;
import android.view.InputEvent;
import android.view.MotionEvent;
@@ -61,9 +63,8 @@
*/
public class PipTouchHandler {
private static final String TAG = "PipTouchHandler";
-
- private static final float STASH_MINIMUM_VELOCITY_X = 3000.f;
private static final float MINIMUM_SIZE_PERCENT = 0.4f;
+ private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f;
// Allow PIP to resize to a slightly bigger state upon touch
private final boolean mEnableResize;
@@ -86,6 +87,8 @@
*/
private boolean mEnableStash = true;
+ private float mStashVelocityThreshold;
+
// The reference inset bounds, used to determine the dismiss fraction
private final Rect mInsetBounds = new Rect();
private int mExpandedShortestEdgeSize;
@@ -175,9 +178,17 @@
mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
mMotionHelper, mainExecutor);
mTouchState = new PipTouchState(ViewConfiguration.get(context),
- () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL,
- mPipBoundsState.getBounds(), true /* allowMenuTimeout */, willResizeMenu(),
- shouldShowResizeHandle()),
+ () -> {
+ if (mPipBoundsState.isStashed()) {
+ animateToUnStashedState();
+ mPipBoundsState.setStashed(STASH_TYPE_NONE);
+ } else {
+ mMenuController.showMenuWithPossibleDelay(MENU_STATE_FULL,
+ mPipBoundsState.getBounds(), true /* allowMenuTimeout */,
+ willResizeMenu(),
+ shouldShowResizeHandle());
+ }
+ },
menuController::hideMenu,
mainExecutor);
@@ -204,6 +215,19 @@
PIP_STASHING, /* defaultValue = */ true);
}
});
+ mStashVelocityThreshold = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ PIP_STASH_MINIMUM_VELOCITY_THRESHOLD,
+ DEFAULT_STASH_VELOCITY_THRESHOLD);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mainExecutor,
+ properties -> {
+ if (properties.getKeyset().contains(PIP_STASH_MINIMUM_VELOCITY_THRESHOLD)) {
+ mStashVelocityThreshold = properties.getFloat(
+ PIP_STASH_MINIMUM_VELOCITY_THRESHOLD,
+ DEFAULT_STASH_VELOCITY_THRESHOLD);
+ }
+ });
}
private void reloadResources() {
@@ -709,6 +733,17 @@
mSavedSnapFraction = -1f;
}
+ private void animateToUnStashedState() {
+ final Rect pipBounds = mPipBoundsState.getBounds();
+ final boolean onLeftEdge = pipBounds.left < mPipBoundsState.getDisplayBounds().left;
+ final Rect unStashedBounds = new Rect(0, pipBounds.top, 0, pipBounds.bottom);
+ unStashedBounds.left = onLeftEdge ? mInsetBounds.left
+ : mInsetBounds.right - pipBounds.width();
+ unStashedBounds.right = onLeftEdge ? mInsetBounds.left + pipBounds.width()
+ : mInsetBounds.right;
+ mMotionHelper.animateToUnStashedBounds(unStashedBounds);
+ }
+
/**
* @return the motion helper.
*/
@@ -772,7 +807,7 @@
}
if (touchState.startedDragging()) {
- mPipBoundsState.setStashed(PipBoundsState.STASH_TYPE_NONE);
+ mPipBoundsState.setStashed(STASH_TYPE_NONE);
mSavedSnapFraction = -1f;
mPipDismissTargetHandler.showDismissTargetMaybe();
}
@@ -825,14 +860,8 @@
// Reset the touch state on up before the fling settles
mTouchState.reset();
- // If user flings the PIP window above the minimum velocity, stash PIP.
- // Only allow stashing to the edge if the user starts dragging the PIP from that
- // edge.
if (mEnableStash && !mPipBoundsState.isStashed()
- && ((vel.x > STASH_MINIMUM_VELOCITY_X
- && mDownSavedFraction > 1f && mDownSavedFraction < 2f)
- || (vel.x < -STASH_MINIMUM_VELOCITY_X
- && mDownSavedFraction > 3f && mDownSavedFraction < 4f))) {
+ && shouldStash(vel, getPossiblyMotionBounds())) {
mMotionHelper.stashToEdge(vel.x, this::stashEndAction /* endAction */);
} else {
mMotionHelper.flingToSnapTarget(vel.x, vel.y,
@@ -846,24 +875,29 @@
&& mPipBoundsState.getBounds().height()
< mPipBoundsState.getMaxSize().y;
if (toExpand) {
+ mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
animateToMaximizedState(null);
} else {
- animateToMinimizedState();
+ animateToUnexpandedState(getUserResizeBounds());
}
- mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
} else {
// Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends
setTouchEnabled(false);
mMotionHelper.expandLeavePip();
}
- } else if (mMenuState != MENU_STATE_FULL && !mPipBoundsState.isStashed()) {
+ } else if (mMenuState != MENU_STATE_FULL) {
if (!mTouchState.isWaitingForDoubleTap()) {
- // User has stalled long enough for this not to be a drag or a double tap, just
- // expand the menu
- mMenuController.showMenu(MENU_STATE_FULL, mPipBoundsState.getBounds(),
- true /* allowMenuTimeout */, willResizeMenu(),
- shouldShowResizeHandle());
+ if (mPipBoundsState.isStashed()) {
+ animateToUnStashedState();
+ mPipBoundsState.setStashed(STASH_TYPE_NONE);
+ } else {
+ // User has stalled long enough for this not to be a drag or a double tap,
+ // just expand the menu
+ mMenuController.showMenu(MENU_STATE_FULL, mPipBoundsState.getBounds(),
+ true /* allowMenuTimeout */, willResizeMenu(),
+ shouldShowResizeHandle());
+ }
} else {
// Next touch event _may_ be the second tap for the double-tap, schedule a
// fallback runnable to trigger the menu if no touch event occurs before the
@@ -894,6 +928,26 @@
mPipExclusionBoundsChangeListener.get().accept(new Rect());
}
}
+
+ private boolean shouldStash(PointF vel, Rect motionBounds) {
+ // If user flings the PIP window above the minimum velocity, stash PIP.
+ // Only allow stashing to the edge if the user starts dragging the PIP from the
+ // opposite edge.
+ final boolean stashFromFlingToEdge = ((vel.x < -mStashVelocityThreshold
+ && mDownSavedFraction > 1f && mDownSavedFraction < 2f)
+ || (vel.x > mStashVelocityThreshold
+ && mDownSavedFraction > 3f && mDownSavedFraction < 4f));
+
+ // If User releases the PIP window while it's out of the display bounds, put
+ // PIP into stashed mode.
+ final int offset = motionBounds.width() / 2;
+ final boolean stashFromDroppingOnEdge =
+ (motionBounds.right > mPipBoundsState.getDisplayBounds().right + offset
+ || motionBounds.left
+ < mPipBoundsState.getDisplayBounds().left - offset);
+
+ return stashFromFlingToEdge || stashFromDroppingOnEdge;
+ }
}
void setPipExclusionBoundsChangeListener(Consumer<Rect> pipExclusionBoundsChangeListener) {
@@ -924,16 +978,21 @@
}
/**
- * @return whether the menu will resize as a part of showing the full menu.
+ * @return {@code true} if the menu should be resized on tap because app explicitly specifies
+ * PiP window size that is too small to hold all the actions.
*/
private boolean willResizeMenu() {
if (!mEnableResize) {
return false;
}
- return mPipBoundsState.getExpandedBounds().width()
- != mPipBoundsState.getNormalBounds().width()
- || mPipBoundsState.getExpandedBounds().height()
- != mPipBoundsState.getNormalBounds().height();
+ final Size estimatedMinMenuSize = mMenuController.getEstimatedMinMenuSize();
+ if (estimatedMinMenuSize == null) {
+ Log.wtf(TAG, "Failed to get estimated menu size");
+ return false;
+ }
+ final Rect currentBounds = mPipBoundsState.getBounds();
+ return currentBounds.width() < estimatedMinMenuSize.getWidth()
+ || currentBounds.height() < estimatedMinMenuSize.getHeight();
}
/**
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
new file mode 100644
index 0000000..e47e1ac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
@@ -0,0 +1,172 @@
+/*
+ * 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.app.ActivityClient;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+
+import com.android.wm.shell.R;
+
+/** Button to restart the size compat activity. */
+class SizeCompatRestartButton extends ImageButton implements View.OnClickListener,
+ View.OnLongClickListener {
+ private static final String TAG = "SizeCompatRestartButton";
+
+ final WindowManager.LayoutParams mWinParams;
+ final boolean mShouldShowHint;
+ final int mDisplayId;
+ final int mPopupOffsetX;
+ final int mPopupOffsetY;
+
+ private IBinder mLastActivityToken;
+ private PopupWindow mShowingHint;
+
+ SizeCompatRestartButton(Context context, int displayId, boolean hasShownHint) {
+ super(context);
+ mDisplayId = displayId;
+ mShouldShowHint = !hasShownHint;
+ final Drawable drawable = context.getDrawable(R.drawable.size_compat_restart_button);
+ setImageDrawable(drawable);
+ setContentDescription(context.getString(R.string.restart_button_description));
+
+ final int drawableW = drawable.getIntrinsicWidth();
+ final int drawableH = drawable.getIntrinsicHeight();
+ mPopupOffsetX = drawableW / 2;
+ mPopupOffsetY = drawableH * 2;
+
+ final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
+ final GradientDrawable mask = new GradientDrawable();
+ mask.setShape(GradientDrawable.OVAL);
+ mask.setColor(color);
+ setBackground(new RippleDrawable(color, null /* content */, mask));
+ setOnClickListener(this);
+ setOnLongClickListener(this);
+
+ mWinParams = new WindowManager.LayoutParams();
+ mWinParams.gravity = getGravity(getResources().getConfiguration().getLayoutDirection());
+ mWinParams.width = drawableW * 2;
+ mWinParams.height = drawableH * 2;
+ mWinParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ mWinParams.format = PixelFormat.TRANSLUCENT;
+ mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ mWinParams.setTitle(SizeCompatRestartButton.class.getSimpleName()
+ + context.getDisplayId());
+ }
+
+ void updateLastTargetActivity(IBinder activityToken) {
+ mLastActivityToken = activityToken;
+ }
+
+ /** @return {@code false} if the target display is invalid. */
+ boolean show() {
+ try {
+ getContext().getSystemService(WindowManager.class).addView(this, mWinParams);
+ } catch (WindowManager.InvalidDisplayException e) {
+ // The target display may have been removed when the callback has just arrived.
+ Log.w(TAG, "Cannot show on display " + getContext().getDisplayId(), e);
+ return false;
+ }
+ return true;
+ }
+
+ void remove() {
+ if (mShowingHint != null) {
+ mShowingHint.dismiss();
+ }
+ getContext().getSystemService(WindowManager.class).removeViewImmediate(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken);
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ showHint();
+ return true;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mShouldShowHint) {
+ showHint();
+ }
+ }
+
+ @Override
+ public void setLayoutDirection(int layoutDirection) {
+ final int gravity = getGravity(layoutDirection);
+ if (mWinParams.gravity != gravity) {
+ mWinParams.gravity = gravity;
+ if (mShowingHint != null) {
+ mShowingHint.dismiss();
+ showHint();
+ }
+ getContext().getSystemService(WindowManager.class).updateViewLayout(this,
+ mWinParams);
+ }
+ super.setLayoutDirection(layoutDirection);
+ }
+
+ void showHint() {
+ if (mShowingHint != null) {
+ return;
+ }
+
+ final View popupView = LayoutInflater.from(getContext()).inflate(
+ R.layout.size_compat_mode_hint, null /* root */);
+ 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 -> popupWindow.dismiss());
+ popupWindow.showAtLocation(this, mWinParams.gravity, mPopupOffsetX, mPopupOffsetY);
+ }
+
+ private 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/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
new file mode 100644
index 0000000..11f22ed
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
@@ -0,0 +1,45 @@
+/*
+ * 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.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.IBinder;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+/**
+ * Interface to engage size compat mode UI.
+ */
+@ExternalThread
+public interface SizeCompatUI {
+ /**
+ * 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.
+ *
+ * @param displayId display the task and activity are in.
+ * @param taskId task the activity is in.
+ * @param taskBounds task bounds to place the restart button in.
+ * @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.
+ */
+ void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
+ @Nullable IBinder sizeCompatActivity,
+ @Nullable ShellTaskOrganizer.TaskListener taskListener);
+}
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
new file mode 100644
index 0000000..8cb16c8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -0,0 +1,192 @@
+/*
+ * 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.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.IBinder;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.ShellExecutor;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Shows a restart-activity button on Task when the foreground activity is in size compatibility
+ * mode.
+ */
+public class SizeCompatUIController implements DisplayController.OnDisplaysChangedListener,
+ DisplayImeController.ImePositionProcessor {
+ private static final String TAG = "SizeCompatUI";
+
+ /** The showing buttons by task id. */
+ private final SparseArray<SizeCompatRestartButton> mActiveButtons = new SparseArray<>(1);
+ /** Avoid creating display context frequently for non-default display. */
+ private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
+
+ @VisibleForTesting
+ final SizeCompatUI mImpl = new SizeCompatUIImpl();
+ private final Context mContext;
+ private final ShellExecutor mMainExecutor;
+ private final DisplayController mDisplayController;
+ private final DisplayImeController mImeController;
+
+ /** Only show once automatically in the process life. */
+ private boolean mHasShownHint;
+
+ /** Creates the {@link SizeCompatUIController}. */
+ public static SizeCompatUI create(Context context,
+ DisplayController displayController,
+ DisplayImeController imeController,
+ ShellExecutor mainExecutor) {
+ return new SizeCompatUIController(context, displayController, imeController, mainExecutor)
+ .mImpl;
+ }
+
+ @VisibleForTesting
+ SizeCompatUIController(Context context,
+ DisplayController displayController,
+ DisplayImeController imeController,
+ ShellExecutor mainExecutor) {
+ mContext = context;
+ mMainExecutor = mainExecutor;
+ mDisplayController = displayController;
+ mImeController = imeController;
+ mDisplayController.addDisplayWindowListener(this);
+ mImeController.addPositionProcessor(this);
+ }
+
+ private void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
+ @Nullable IBinder sizeCompatActivity,
+ @Nullable ShellTaskOrganizer.TaskListener taskListener) {
+ // TODO Draw button on Task surface
+ if (taskBounds == null || sizeCompatActivity == null || taskListener == null) {
+ // Null token means the current foreground activity is not in size compatibility mode.
+ removeRestartButton(taskId);
+ } else {
+ updateRestartButton(displayId, taskId, sizeCompatActivity);
+ }
+ }
+
+ // TODO move from SizeCompatModeActivityController from system UI.
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ mDisplayContextCache.remove(displayId);
+ for (int i = 0; i < mActiveButtons.size(); i++) {
+ final int taskId = mActiveButtons.keyAt(i);
+ final SizeCompatRestartButton button = mActiveButtons.get(taskId);
+ if (button != null && button.mDisplayId == displayId) {
+ removeRestartButton(taskId);
+ }
+ }
+ }
+
+ @Override
+ public void onImeVisibilityChanged(int displayId, boolean isShowing) {
+ final int newVisibility = isShowing ? View.GONE : View.VISIBLE;
+ for (int i = 0; i < mActiveButtons.size(); i++) {
+ final int taskId = mActiveButtons.keyAt(i);
+ final SizeCompatRestartButton button = mActiveButtons.get(taskId);
+ if (button == null || button.mDisplayId != displayId) {
+ continue;
+ }
+
+ // Hide the button when input method is showing.
+ if (button.getVisibility() != newVisibility) {
+ button.setVisibility(newVisibility);
+ }
+ }
+ }
+
+ private void updateRestartButton(int displayId, int taskId, IBinder activityToken) {
+ SizeCompatRestartButton restartButton = mActiveButtons.get(taskId);
+ if (restartButton != null) {
+ restartButton.updateLastTargetActivity(activityToken);
+ return;
+ }
+
+ final Context context = getOrCreateDisplayContext(displayId);
+ if (context == null) {
+ Log.i(TAG, "Cannot get context for display " + displayId);
+ return;
+ }
+
+ restartButton = createRestartButton(context, displayId);
+ restartButton.updateLastTargetActivity(activityToken);
+ if (restartButton.show()) {
+ mActiveButtons.append(taskId, restartButton);
+ } else {
+ onDisplayRemoved(displayId);
+ }
+ }
+
+ @VisibleForTesting
+ SizeCompatRestartButton createRestartButton(Context context, int displayId) {
+ final SizeCompatRestartButton button = new SizeCompatRestartButton(context, displayId,
+ mHasShownHint);
+ // Only show hint for the first time.
+ mHasShownHint = true;
+ return button;
+ }
+
+ private void removeRestartButton(int taskId) {
+ final SizeCompatRestartButton button = mActiveButtons.get(taskId);
+ if (button != null) {
+ button.remove();
+ mActiveButtons.remove(taskId);
+ }
+ }
+
+ private Context getOrCreateDisplayContext(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return mContext;
+ }
+ Context context = null;
+ final WeakReference<Context> ref = mDisplayContextCache.get(displayId);
+ if (ref != null) {
+ context = ref.get();
+ }
+ if (context == null) {
+ Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
+ if (display != null) {
+ context = mContext.createDisplayContext(display);
+ mDisplayContextCache.put(displayId, new WeakReference<>(context));
+ }
+ }
+ return context;
+ }
+
+ private class SizeCompatUIImpl implements SizeCompatUI {
+ @Override
+ public void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
+ @Nullable IBinder sizeCompatActivity,
+ @Nullable ShellTaskOrganizer.TaskListener taskListener) {
+ mMainExecutor.execute(() ->
+ SizeCompatUIController.this.onSizeCompatInfoChanged(displayId, taskId,
+ taskBounds, sizeCompatActivity, taskListener));
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 7c1b9d8..2c68092 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -18,12 +18,16 @@
import android.annotation.IntDef;
import android.app.ActivityManager;
+import android.app.PendingIntent;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import java.io.PrintWriter;
@@ -31,46 +35,93 @@
* Interface to engage split-screen feature.
*/
@ExternalThread
-public interface SplitScreen {
+public interface SplitScreen extends DragAndDropPolicy.Starter {
/**
- * Specifies that the side-stage is positioned at the top half of the screen if
+ * Stage position isn't specified normally meaning to use what ever it is currently set to.
+ */
+ int STAGE_POSITION_UNDEFINED = -1;
+ /**
+ * Specifies that a stage is positioned at the top half of the screen if
* in portrait mode or at the left half of the screen if in landscape mode.
*/
- int SIDE_STAGE_POSITION_TOP_OR_LEFT = 0;
+ int STAGE_POSITION_TOP_OR_LEFT = 0;
/**
- * Specifies that the side-stage is positioned at the bottom half of the screen if
+ * Specifies that a stage is positioned at the bottom half of the screen if
* in portrait mode or at the right half of the screen if in landscape mode.
*/
- int SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
+ int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
- @IntDef(prefix = { "SIDE_STAGE_POSITION_" }, value = {
- SIDE_STAGE_POSITION_TOP_OR_LEFT,
- SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT
+ @IntDef(prefix = { "STAGE_POSITION_" }, value = {
+ STAGE_POSITION_UNDEFINED,
+ STAGE_POSITION_TOP_OR_LEFT,
+ STAGE_POSITION_BOTTOM_OR_RIGHT
})
- @interface SideStagePosition {}
+ @interface StagePosition {}
+
+ /**
+ * Stage type isn't specified normally meaning to use what ever the default is.
+ * E.g. exit split-screen and launch the app in fullscreen.
+ */
+ int STAGE_TYPE_UNDEFINED = -1;
+ /**
+ * The main stage type.
+ * @see MainStage
+ */
+ int STAGE_TYPE_MAIN = 0;
+
+ /**
+ * The side stage type.
+ * @see SideStage
+ */
+ int STAGE_TYPE_SIDE = 1;
+
+ @IntDef(prefix = { "STAGE_TYPE_" }, value = {
+ STAGE_TYPE_UNDEFINED,
+ STAGE_TYPE_MAIN,
+ STAGE_TYPE_SIDE
+ })
+ @interface StageType {}
+
+ /** Callback interface for listening to changes in a split-screen stage. */
+ interface SplitScreenListener {
+ void onStagePositionChanged(@StageType int stage, @StagePosition int position);
+ void onTaskStageChanged(int taskId, @StageType int stage);
+ }
/** @return {@code true} if split-screen is currently visible. */
boolean isSplitScreenVisible();
/** Moves a task in the side-stage of split-screen. */
- boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition);
+ boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition);
/** Moves a task in the side-stage of split-screen. */
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SideStagePosition int sideStagePosition);
+ @StagePosition int sideStagePosition);
/** Removes a task from the side-stage of split-screen. */
boolean removeFromSideStage(int taskId);
/** Sets the position of the side-stage. */
- void setSideStagePosition(@SideStagePosition int sideStagePosition);
+ void setSideStagePosition(@StagePosition int sideStagePosition);
/** Hides the side-stage if it is currently visible. */
void setSideStageVisibility(boolean visible);
+ default void enterSplitScreen(int taskId, boolean leftOrTop) {
+ moveToSideStage(taskId,
+ leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
+ }
/** Removes the split-screen stages. */
void exitSplitScreen();
/** Gets the stage bounds. */
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
- /** Updates the launch activity options for the split position we want to launch it in. */
- void updateActivityOptions(Bundle opts, @SideStagePosition int position);
/** Dumps current status of split-screen. */
void dump(@NonNull PrintWriter pw, String prefix);
/** Called when the shell organizer has been registered. */
void onOrganizerRegistered();
+
+ void registerSplitScreenListener(SplitScreenListener listener);
+ void unregisterSplitScreenListener(SplitScreenListener listener);
+
+ void startTask(int taskId,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId, @StageType int stage,
+ @StagePosition int position, @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 27d3b81..18dd53b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -19,11 +19,19 @@
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.pm.LauncherApps;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -69,7 +77,7 @@
}
@Override
- public boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition) {
+ public boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
@@ -79,7 +87,7 @@
@Override
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SideStagePosition int sideStagePosition) {
+ @StagePosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
@@ -89,7 +97,7 @@
}
@Override
- public void setSideStagePosition(@SideStagePosition int sideStagePosition) {
+ public void setSideStagePosition(@StagePosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition);
}
@@ -109,8 +117,103 @@
}
@Override
- public void updateActivityOptions(Bundle opts, @SideStagePosition int position) {
- mStageCoordinator.updateActivityOptions(opts, position);
+ public void registerSplitScreenListener(SplitScreenListener listener) {
+ mStageCoordinator.registerSplitScreenListener(listener);
+ }
+
+ @Override
+ public void unregisterSplitScreenListener(SplitScreenListener listener) {
+ mStageCoordinator.unregisterSplitScreenListener(listener);
+ }
+
+ @Override
+ public void startTask(int taskId,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ options = resolveStartStage(stage, position, options);
+
+ try {
+ ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to launch task", e);
+ }
+ }
+
+ @Override
+ public void startShortcut(String packageName, String shortcutId, @StageType int stage,
+ @StagePosition int position, @Nullable Bundle options, UserHandle user) {
+ options = resolveStartStage(stage, position, options);
+
+ try {
+ LauncherApps launcherApps =
+ mContext.getSystemService(LauncherApps.class);
+ launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
+ options, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "Failed to launch shortcut", e);
+ }
+ }
+
+ @Override
+ public void startIntent(PendingIntent intent,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ options = resolveStartStage(stage, position, options);
+
+ try {
+ intent.send(null, 0, null, null, null, null, options);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "Failed to launch activity", e);
+ }
+ }
+
+ private Bundle resolveStartStage(@StageType int stage, @StagePosition int position,
+ @Nullable Bundle options) {
+ switch (stage) {
+ case STAGE_TYPE_UNDEFINED: {
+ // Use the stage of the specified position is valid.
+ if (position != STAGE_POSITION_UNDEFINED) {
+ if (position == mStageCoordinator.getSideStagePosition()) {
+ options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
+ } else {
+ options = resolveStartStage(STAGE_TYPE_MAIN, position, options);
+ }
+ } else {
+ // Exit split-screen and launch fullscreen since stage wasn't specified.
+ mStageCoordinator.exitSplitScreen();
+ }
+ break;
+ }
+ case STAGE_TYPE_SIDE: {
+ if (position != STAGE_POSITION_UNDEFINED) {
+ mStageCoordinator.setSideStagePosition(position);
+ } else {
+ position = mStageCoordinator.getSideStagePosition();
+ }
+ if (options == null) {
+ options = new Bundle();
+ }
+ mStageCoordinator.updateActivityOptions(options, position);
+ break;
+ }
+ case STAGE_TYPE_MAIN: {
+ if (position != STAGE_POSITION_UNDEFINED) {
+ // Set the side stage opposite of what we want to the main stage.
+ final int sideStagePosition = position == STAGE_POSITION_TOP_OR_LEFT
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+ mStageCoordinator.setSideStagePosition(sideStagePosition);
+ } else {
+ position = mStageCoordinator.getMainStagePosition();
+ }
+ if (options == null) {
+ options = new Bundle();
+ }
+ mStageCoordinator.updateActivityOptions(options, position);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown stage=" + stage);
+ }
+
+ return options;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index d571e75..176852b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -17,12 +17,14 @@
package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.content.Context;
@@ -41,6 +43,8 @@
import com.android.wm.shell.common.split.SplitLayout;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -64,8 +68,7 @@
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
private final SideStage mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
- private @SplitScreen.SideStagePosition int mSideStagePosition =
- SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ private @SplitScreen.StagePosition int mSideStagePosition = STAGE_POSITION_BOTTOM_OR_RIGHT;
private final int mDisplayId;
private SplitLayout mSplitLayout;
@@ -75,6 +78,7 @@
private final ShellTaskOrganizer mTaskOrganizer;
private DisplayAreaInfo mDisplayAreaInfo;
private final Context mContext;
+ private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) {
@@ -107,7 +111,7 @@
}
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitScreen.SideStagePosition int sideStagePosition) {
+ @SplitScreen.StagePosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSideStagePosition = sideStagePosition;
mMainStage.activate(getMainStageBounds(), wct);
@@ -130,7 +134,16 @@
return result;
}
- void setSideStagePosition(@SplitScreen.SideStagePosition int sideStagePosition) {
+ @SplitScreen.StagePosition int getSideStagePosition() {
+ return mSideStagePosition;
+ }
+
+ @SplitScreen.StagePosition int getMainStagePosition() {
+ return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+ }
+
+ void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
mSideStagePosition = sideStagePosition;
if (mSideStageListener.mVisible) {
onStageVisibilityChanged(mSideStageListener);
@@ -163,7 +176,7 @@
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
}
- void updateActivityOptions(Bundle opts, @SplitScreen.SideStagePosition int position) {
+ void updateActivityOptions(Bundle opts, @SplitScreen.StagePosition int position) {
final StageTaskListener stage = position == mSideStagePosition ? mSideStage : mMainStage;
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
@@ -176,6 +189,35 @@
}
}
+ void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
+ if (mListeners.contains(listener)) return;
+ mListeners.add(listener);
+ listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
+ listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
+ mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
+ mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
+ }
+
+ void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
+ mListeners.remove(listener);
+ }
+
+ private void onStageChildTaskStatusChanged(
+ StageListenerImpl stageListener, int taskId, boolean present) {
+
+ int stage;
+ if (present) {
+ stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ } else {
+ // No longer on any stage
+ stage = STAGE_TYPE_UNDEFINED;
+ }
+
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onTaskStageChanged(taskId, stage);
+ }
+ }
+
private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -299,7 +341,7 @@
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
final boolean mainStageToTop = bottomOrRight
- && mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ && mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
exitSplitScreen(mainStageToTop ? mMainStage : mSideStage);
}
@@ -326,8 +368,8 @@
@Override
public void onDoubleTappedDivider() {
- setSideStagePosition(mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
- ? SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT : SIDE_STAGE_POSITION_TOP_OR_LEFT);
+ setSideStagePosition(mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT);
}
@Override
@@ -380,12 +422,12 @@
}
private Rect getSideStageBounds() {
- return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
+ return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
}
private Rect getMainStageBounds() {
- return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
+ return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
}
@@ -429,6 +471,11 @@
}
@Override
+ public void onChildTaskStatusChanged(int taskId, boolean present) {
+ StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present);
+ }
+
+ @Override
public void onRootTaskVanished() {
reset();
StageCoordinator.this.onStageRootTaskVanished(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 1aa7552..6532993 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -58,6 +58,7 @@
public interface StageListenerCallbacks {
void onRootTaskAppeared();
void onStatusChanged(boolean visible, boolean hasChildren);
+ void onChildTaskStatusChanged(int taskId, boolean present);
void onRootTaskVanished();
}
private final StageListenerCallbacks mCallbacks;
@@ -83,9 +84,11 @@
mRootTaskInfo = taskInfo;
mCallbacks.onRootTaskAppeared();
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
- mChildrenLeashes.put(taskInfo.taskId, leash);
- mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
+ final int taskId = taskInfo.taskId;
+ mChildrenLeashes.put(taskId, leash);
+ mChildrenTaskInfo.put(taskId, taskInfo);
updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
+ mCallbacks.onChildTaskStatusChanged(taskId, true /* present */);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -120,6 +123,7 @@
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
sendStatusChanged();
+ mCallbacks.onChildTaskStatusChanged(taskId, false /* present */);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -134,6 +138,13 @@
wct.reorder(mRootTaskInfo.token, visible /* onTop */);
}
+ void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
+ @SplitScreen.StageType int stage) {
+ for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+ listener.onTaskStageChanged(mChildrenTaskInfo.keyAt(i), stage);
+ }
+ }
+
private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl leash, boolean firstAppeared) {
final Point taskPositionInParent = taskInfo.positionInParent;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 3198725..b5e1896 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -220,9 +220,8 @@
final InputChannel tmpInputChannel = new InputChannel();
mainExecutor.execute(() -> {
try {
- final int res = session.addToDisplay(window, layoutParams, View.GONE,
- displayId, mTmpInsetsState, tmpFrames.frame, tmpInputChannel,
- mTmpInsetsState, mTempControls);
+ final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
+ mTmpInsetsState, tmpInputChannel, mTmpInsetsState, mTempControls);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return;
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 4a498d2..a57ac35 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -18,7 +18,7 @@
name: "WMShellFlickerTests",
srcs: ["src/**/*.java", "src/**/*.kt"],
manifest: "AndroidManifest.xml",
- test_config: "AndroidTestPhysicalDevices.xml",
+ test_config: "AndroidTest.xml",
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
@@ -36,25 +36,3 @@
"wmshell-flicker-test-components",
],
}
-
-android_test {
- name: "WMShellFlickerTestsVirtual",
- srcs: ["src/**/*.java", "src/**/*.kt"],
- manifest: "AndroidManifest.xml",
- test_config: "AndroidTestVirtualDevices.xml",
- platform_apis: true,
- certificate: "platform",
- libs: ["android.test.runner"],
- static_libs: [
- "androidx.test.ext.junit",
- "flickerlib",
- "truth-prebuilt",
- "app-helpers-core",
- "launcher-helper-lib",
- "launcher-aosp-tapl",
- "wm-flicker-common-assertions",
- "wm-flicker-common-app-helpers",
- "platform-test-annotations",
- "wmshell-flicker-test-components",
- ],
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml
rename to libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
deleted file mode 100644
index 0738608..0000000
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright 2020 Google Inc. All Rights Reserved.
- -->
-<configuration description="Runs WindowManager Shell Flicker Tests">
- <option name="test-tag" value="FlickerTests" />
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on" />
- <!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true" />
- <!-- set WM tracing verbose level to all -->
- <option name="run-command" value="cmd window tracing level all" />
- <!-- inform WM to log all transactions -->
- <option name="run-command" value="cmd window tracing transaction" />
- <!-- restart launcher to activate TAPL -->
- <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
- <!-- reboot the device to teardown any crashed tests -->
- <option name="cleanup-action" value="REBOOT" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="WMShellFlickerTests.apk"/>
- <option name="test-file-name" value="WMShellFlickerTestApp.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.wm.shell.flicker"/>
- <option name="exclude-annotation" value="androidx.test.filters.RequiresDevice" />
- <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
- <option name="shell-timeout" value="6600s" />
- <option name="test-timeout" value="6000s" />
- <option name="hidden-api-checks" value="false" />
- </test>
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/sdcard/flicker" />
- <option name="collect-on-run-ended-only" value="true" />
- <option name="clean-up" value="true" />
- </metrics_collector>
-</configuration>
\ No newline at end of file
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 ccfdce6..7ad7553 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,33 +18,34 @@
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.helpers.WindowUtils
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.FlickerTestBase.Companion.DOCKED_STACK_DIVIDER
@JvmOverloads
-fun LayersAssertionBuilder.appPairsDividerIsVisible(
+fun LayersAssertionBuilderLegacy.appPairsDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
end("appPairsDividerIsVisible", bugId, enabled) {
- this.isVisible(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ this.isVisible(APP_PAIR_SPLIT_DIVIDER)
}
}
@JvmOverloads
-fun LayersAssertionBuilder.appPairsDividerIsInvisible(
+fun LayersAssertionBuilderLegacy.appPairsDividerIsInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
end("appPairsDividerIsInVisible", bugId, enabled) {
- this.notExists(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ this.notExists(APP_PAIR_SPLIT_DIVIDER)
}
}
@JvmOverloads
-fun LayersAssertionBuilder.appPairsDividerBecomesVisible(
+fun LayersAssertionBuilderLegacy.appPairsDividerBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -56,7 +57,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerIsVisible(
+fun LayersAssertionBuilderLegacy.dockedStackDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -66,7 +67,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(
+fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -78,7 +79,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(
+fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -90,7 +91,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackDividerIsInvisible(
+fun LayersAssertionBuilderLegacy.dockedStackDividerIsInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -100,33 +101,33 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible(
+fun LayersAssertionBuilderLegacy.appPairsPrimaryBoundsIsVisible(
rotation: Int,
primaryLayerName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
end("PrimaryAppBounds", bugId, enabled) {
- val dividerRegion = entry.getVisibleBounds(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
}
}
@JvmOverloads
-fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible(
+fun LayersAssertionBuilderLegacy.appPairsSecondaryBoundsIsVisible(
rotation: Int,
secondaryLayerName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
end("SecondaryAppBounds", bugId, enabled) {
- val dividerRegion = entry.getVisibleBounds(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
}
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible(
+fun LayersAssertionBuilderLegacy.dockedStackPrimaryBoundsIsVisible(
rotation: Int,
primaryLayerName: String,
bugId: Int = 0,
@@ -139,7 +140,7 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible(
+fun LayersAssertionBuilderLegacy.dockedStackSecondaryBoundsIsVisible(
rotation: Int,
secondaryLayerName: String,
bugId: Int = 0,
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 3953c1c..89bbdb0 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
@@ -135,12 +135,4 @@
throw RuntimeException(e)
}
}
-
- companion object {
- const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
- const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
- const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
- const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
- const val IMAGE_WALLPAPER = "ImageWallpaper"
- }
}
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 564a418..d257749 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
@@ -21,7 +21,6 @@
import android.os.SystemClock
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -46,10 +45,8 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AppPairsTestCannotPairNonResizeableApps(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
@@ -83,9 +80,8 @@
}
}
- return FlickerTestRunnerFactory(instrumentation,
- repetitions = AppPairsHelper.TEST_REPETITIONS)
- .buildTest(transition, testSpec)
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ transition, testSpec, 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 f63eb1d..257350b 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
@@ -21,13 +21,12 @@
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
+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.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.FlickerTestBase
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import org.junit.FixMethodOrder
@@ -44,10 +43,8 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AppPairsTestPairPrimaryAndSecondaryApps(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
@@ -67,8 +64,7 @@
layersTrace {
appPairsDividerIsVisible()
end("appsEndingBounds", enabled = false) {
- val dividerRegion = entry.getVisibleBounds(
- FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
this.hasVisibleRegion(primaryApp.defaultWindowName,
appPairsHelper.getPrimaryBounds(dividerRegion))
.hasVisibleRegion(secondaryApp.defaultWindowName,
@@ -83,8 +79,8 @@
}
}
}
- return FlickerTestRunnerFactory(instrumentation,
- repetitions = AppPairsHelper.TEST_REPETITIONS).buildTest(transition, testSpec)
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, transition,
+ testSpec, 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 731d998..0b001f5 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
@@ -21,13 +21,12 @@
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
+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.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.FlickerTestBase
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import org.junit.FixMethodOrder
@@ -44,10 +43,8 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AppPairsTestUnpairPrimaryAndSecondaryApps(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
@@ -72,8 +69,7 @@
layersTrace {
appPairsDividerIsInvisible()
start("appsStartingBounds", enabled = false) {
- val dividerRegion = entry.getVisibleBounds(
- FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
this.hasVisibleRegion(primaryApp.defaultWindowName,
appPairsHelper.getPrimaryBounds(dividerRegion))
.hasVisibleRegion(secondaryApp.defaultWindowName,
@@ -92,8 +88,8 @@
}
}
}
- return FlickerTestRunnerFactory(instrumentation,
- repetitions = AppPairsHelper.TEST_REPETITIONS).buildTest(transition, testSpec)
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, transition,
+ testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS)
}
}
}
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 da3450b..aafa9bf 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
@@ -22,17 +22,14 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
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.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
@@ -54,16 +51,13 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class RotateTwoLaunchedAppsInAppPairsMode(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object : RotateTwoLaunchedAppsTransition(
InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
buildTestTag("testRotateTwoLaunchedAppsInAppPairsMode", configuration)
@@ -77,13 +71,14 @@
assertions {
layersTrace {
navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation)
- appPairsDividerIsVisible()
+ enabled = false)
+ statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation,
+ enabled = false)
+ appPairsDividerIsVisible(enabled = false)
appPairsPrimaryBoundsIsVisible(configuration.endRotation,
- primaryApp.defaultWindowName, 172776659)
+ primaryApp.defaultWindowName, bugId = 172776659)
appPairsSecondaryBoundsIsVisible(configuration.endRotation,
- secondaryApp.defaultWindowName, 172776659)
+ secondaryApp.defaultWindowName, bugId = 172776659)
}
windowManagerTrace {
navBarWindowIsAlwaysVisible()
@@ -96,10 +91,10 @@
}
}
- return FlickerTestRunnerFactory(instrumentation,
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ transition, testSpec,
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)
- ).buildTest(transition, testSpec)
+ 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 05543fd..19ca31f 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
@@ -22,7 +22,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -54,16 +53,13 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object : RotateTwoLaunchedAppsTransition(
InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
buildTestTag("testRotateAndEnterAppPairsMode", configuration)
@@ -96,10 +92,11 @@
}
}
- return FlickerTestRunnerFactory(instrumentation,
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ transition, testSpec,
repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)
- ).buildTest(transition, testSpec)
+ )
}
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
deleted file mode 100644
index dea5c30..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,208 +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.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.canSplitScreen
-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.openQuickstep
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
-import com.android.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.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
-import org.junit.Assert
-import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test SplitScreen launch.
- * To run this test: `atest WMShellFlickerTests:EnterLegacySplitScreenTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- private val splitScreenSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- val testLaunchActivity = "launch_splitScreen_test_activity"
- withTestName {
- testLaunchActivity
- }
- setup {
- eachRun {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- }
- }
- teardown {
- eachRun {
- if (uiDevice.isInSplitScreen()) {
- uiDevice.exitSplitScreen()
- }
- splitScreenApp.exit()
- secondaryApp.exit()
- nonResizeableApp.exit()
- }
- }
- assertions {
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- }
- }
-
- @Test
- fun testEnterSplitScreen_dockActivity() {
- val testTag = "testEnterSplitScreen_dockActivity"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- }
- assertions {
- layersTrace {
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- dockedStackDividerBecomesVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- LIVE_WALLPAPER_PACKAGE_NAME)
- )
- }
- windowManagerTrace {
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- @Test
- fun testEnterSplitScreen_launchToSide() {
- val testTag = "testEnterSplitScreen_launchToSide"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- secondaryApp.launchViaIntent()
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- splitScreenApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- dockedStackSecondaryBoundsIsVisible(
- rotation, secondaryApp.defaultWindowName, 169271943)
- dockedStackDividerBecomesVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
- }
- windowManagerTrace {
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
- }
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName))
- }
- }
- }
- }
-
- @FlakyTest(bugId = 173875043)
- @Test
- fun testNonResizeableNotDocked() {
- val testTag = "testNonResizeableNotDocked"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- nonResizeableApp.launchViaIntent()
- uiDevice.openQuickstep()
- if (uiDevice.canSplitScreen()) {
- Assert.fail("Non-resizeable app should not enter split screen")
- }
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, nonResizeableApp.defaultWindowName)
- )
- }
- windowManagerTrace {
- end("appWindowIsVisible") {
- isInvisible(nonResizeableApp.defaultWindowName)
- }
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, nonResizeableApp.defaultWindowName))
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-}
\ No newline at end of file
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
new file mode 100644
index 0000000..5374bd9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.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.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
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open activity and dock to primary split screen
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenDockActivity`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterSplitScreenDockActivity(
+ 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("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,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+}
\ 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
new file mode 100644
index 0000000..d750403
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.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.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.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * 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)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterSplitScreenLaunchToSide(
+ 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("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,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+}
\ 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
new file mode 100644
index 0000000..e361923
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.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.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
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assert
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * 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)
+@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)
+ }
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, defaultTransitionSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 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
new file mode 100644
index 0000000..7782364
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+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.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
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * 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)
+@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
+ )
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, defaultTransitionSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt
deleted file mode 100644
index 4ddfb3e..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt
+++ /dev/null
@@ -1,93 +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.legacysplitscreen
-
-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.Flicker
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
-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
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.repetitions
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenFromBottomTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitLegacySplitScreenFromBottomTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
- // TODO(b/162923992) Use of multiple segments of flicker spec for testing
- return FlickerTestRunnerFactory(instrumentation,
- listOf(Surface.ROTATION_0, Surface.ROTATION_90))
- .buildTest { configuration ->
- withTestName {
- buildTestTag("exitSplitScreenFromBottom", configuration)
- }
- repeat { configuration.repetitions }
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview()
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen()
- device.waitForIdle()
- this.setRotation(configuration.endRotation)
- }
- }
- teardown {
- eachRun {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- splitScreenApp.exit()
- }
- }
- transitions {
- device.exitSplitScreenFromBottom()
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
deleted file mode 100644
index 701b0d0..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,153 +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.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.util.Rational
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.resizeSplitScreen
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test exit SplitScreen mode.
- * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- private val splitScreenSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- val testLaunchActivity = "launch_splitScreen_test_activity"
- withTestName {
- testLaunchActivity
- }
- setup {
- eachRun {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- secondaryApp.launchViaIntent()
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- }
- }
- teardown {
- eachRun {
- splitScreenApp.exit()
- secondaryApp.exit()
- }
- }
- assertions {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME))
- }
- }
- }
-
- @Test
- fun testEnterSplitScreen_exitPrimarySplitScreenMode() {
- val testTag = "testEnterSplitScreen_exitPrimarySplitScreenMode"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- uiDevice.exitSplitScreen()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- layerBecomesInvisible(splitScreenApp.defaultWindowName)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsInvisible") {
- isInvisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- @Test
- @FlakyTest(bugId = 172811376)
- fun testEnterSplitScreen_exitPrimary_showSecondaryAppFullScreen() {
- val testTag = "testEnterSplitScreen_exitPrimary_showSecondaryAppFullScreen"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- splitScreenApp.reopenAppFromOverview()
- uiDevice.resizeSplitScreen(startRatio)
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- companion object {
- private val startRatio = Rational(1, 3)
-
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-}
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
new file mode 100644
index 0000000..59f6aaf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -0,0 +1,93 @@
+/*
+ * 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 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.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.layerBecomesInvisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test dock activity to primary split screen, and open secondary to side, exit primary split
+ * and test secondary activity become full screen.
+ * To run this test: `atest WMShellFlickerTests:ExitPrimarySplitScreenShowSecondaryFullscreen`
+ */
+@RequiresDevice
+@RunWith(Parameterized::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
+ )
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, defaultTransitionSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+}
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
new file mode 100644
index 0000000..03b6edf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.legacysplitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.support.test.launcherhelper.LauncherStrategyFactory
+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.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
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.layerBecomesInvisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.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.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest WMShellFlickerTests:LegacySplitScreenToLauncher`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class LegacySplitScreenToLauncher(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ 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)
+
+ // 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)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncherTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncherTest.kt
deleted file mode 100644
index 43a7565..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncherTest.kt
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.support.test.launcherhelper.LauncherStrategyFactory
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-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
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.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.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:LegacySplitScreenToLauncherTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class LegacySplitScreenToLauncherTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
- 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)
-
- // b/161435597 causes the test not to work on 90 degrees
- return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
- .buildTest { 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)
- }
- }
- }
- }
- }
-}
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
new file mode 100644
index 0000000..328ff88
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -0,0 +1,99 @@
+/*
+ * 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/LICENSE2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.app.Instrumentation
+import android.os.Bundle
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.view.Surface
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import 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)
+ .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
+ get() = { configuration ->
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ device.openQuickStepAndClearRecentAppsFromOverview()
+ secondaryApp.launchViaIntent(wmHelper)
+ splitScreenApp.launchViaIntent(wmHelper)
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ teardown {
+ eachRun {
+ splitScreenApp.exit()
+ secondaryApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
+
+ internal open val cleanSetup: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ device.openQuickStepAndClearRecentAppsFromOverview()
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ teardown {
+ eachRun {
+ nonResizeableApp.exit()
+ 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)
+ }
+ }
+ }
+}
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
new file mode 100644
index 0000000..f2a7cda
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.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.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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
+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.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launch non resizable activity in split screen mode will trigger exit split screen mode
+ * (Non resizable activity launch via recent overview)
+ * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreen`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::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 {
+ 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
+ )
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, cleanSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
deleted file mode 100644
index 6fca580..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-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
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreenTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class NonResizableDismissInLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
-
- @Test
- fun testNonResizableDismissInLegacySplitScreenTest() {
- val testTag = "testNonResizableDismissInLegacySplitScreenTest"
-
- runWithFlicker(transitionSetup) {
- withTestName { testTag }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- nonResizeableApp.launchViaIntent(wmHelper)
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen()
- nonResizeableApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- end("appsEndingBounds", enabled = false) {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds)
- }
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName, LETTER_BOX_NAME,
- TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- end("nonResizeableAppWindowIsVisible") {
- isVisible(nonResizeableApp.defaultWindowName)
- .isInvisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- 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) }
- }
- }
-}
\ 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
new file mode 100644
index 0000000..421ecff
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.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.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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
+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.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launch non resizable activity in split screen mode will trigger exit split screen mode
+ * (Non resizable activity launch via intent)
+ * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreen`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::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 {
+ 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
+ )
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, cleanSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
deleted file mode 100644
index deae41f..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-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
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreenTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class NonResizableLaunchInLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
-
- @Test
- fun testNonResizableLaunchInLegacySplitScreenTest() {
- val testTag = "testNonResizableLaunchInLegacySplitScreenTest"
-
- runWithFlicker(transitionSetup) {
- withTestName { testTag }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- nonResizeableApp.launchViaIntent(wmHelper)
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen()
- nonResizeableApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- end("appsEndingBounds", enabled = false) {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds)
- }
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName, LETTER_BOX_NAME,
- TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- end("nonResizeableAppWindowIsVisible") {
- isVisible(nonResizeableApp.defaultWindowName)
- .isInvisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- 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) }
- }
- }
-}
\ No newline at end of file
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
new file mode 100644
index 0000000..c802ffe
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.legacysplitscreen
+
+import android.os.Bundle
+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.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
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreen`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppToLegacySplitScreen(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @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,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt
deleted file mode 100644
index 6200a69..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,109 +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.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.appWindowBecomesVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-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
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreenTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppToLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- @Test
- fun OpenAppToLegacySplitScreenTest() {
- val testTag = "OpenAppToLegacySplitScreenTest"
- val helper = WindowManagerStateHelper()
- runWithFlicker(transitionSetup) {
- withTestName { testTag }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- setup {
- eachRun {
- splitScreenApp.launchViaIntent(wmHelper)
- device.pressHome()
- this.setRotation(rotation)
- }
- }
- transitions {
- device.launchSplitScreen()
- helper.waitForAppTransitionIdle()
- }
- assertions {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- LETTER_BOX_NAME)
- )
- appWindowBecomesVisible(splitScreenApp.getPackage())
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation, enabled = false)
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- LETTER_BOX_NAME))
- appPairsDividerBecomesVisible()
- layerBecomesVisible(splitScreenApp.getPackage())
- }
-
- eventLog {
- focusChanges(splitScreenApp.`package`,
- "recents_animation_input_consumer", "NexusLauncherActivity",
- bugId = 151179149)
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- // TODO(b/161435597) causes the test not to work on 90 degrees
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
similarity index 96%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index f6b0772..54a37d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -25,7 +25,6 @@
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.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.endRotation
@@ -59,7 +58,7 @@
/**
* Test split screen resizing window transitions.
- * To run this test: `atest WMShellFlickerTests:ResizeLegacySplitScreenTest`
+ * To run this test: `atest WMShellFlickerTests:ResizeLegacySplitScreen`
*
* Currently it runs only in 0 degrees because of b/156100803
*/
@@ -68,11 +67,9 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 159096424)
-class ResizeLegacySplitScreenTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+class ResizeLegacySplitScreen(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
private const val sSimpleActivity = "SimpleActivity"
private const val sImeActivity = "ImeActivity"
@@ -86,9 +83,8 @@
val testAppTop = SimpleAppHelper(instrumentation)
val testAppBottom = ImeAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0))
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
withTestName {
val description = (startRatio.toString().replace("/", "-") + "_to_" +
stopRatio.toString().replace("/", "-"))
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
new file mode 100644
index 0000000..214269e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+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.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
+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.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test dock activity to primary split screen and rotate
+ * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppAndEnterSplitScreen`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class RotateOneLaunchedAppAndEnterSplitScreen(
+ 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("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 */))
+ }
+ }
+}
\ 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
new file mode 100644
index 0000000..4290c92
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+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.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
+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.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Rotate
+ * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppInSplitScreenMode`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class RotateOneLaunchedAppInSplitScreenMode(
+ 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("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,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt
deleted file mode 100644
index 07571c3..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
-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.wakeUpAndGoToHomeScreen
-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.dockedStackDividerIsVisible
-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
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class RotateOneLaunchedAppTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- private val splitScreenRotationSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- val testSetupRotation = "testSetupRotation"
- withTestName {
- testSetupRotation
- }
- setup {
- test {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- }
- }
- teardown {
- eachRun {
- if (uiDevice.isInSplitScreen()) {
- uiDevice.exitSplitScreen()
- }
- setRotation(Surface.ROTATION_0)
- splitScreenApp.exit()
- secondaryApp.exit()
- }
- }
- }
-
- @Test
- fun testRotateInSplitScreenMode() {
- val testTag = "testEnterSplitScreen_launchToSide"
- runWithFlicker(splitScreenRotationSetup) {
- withTestName { testTag }
- repeat {
- SplitScreenHelper.TEST_REPETITIONS
- }
- transitions {
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- setRotation(rotation)
- }
- assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943)
- dockedStackDividerIsVisible()
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- @Test
- fun testRotateAndEnterSplitScreenMode() {
- val testTag = "testRotateAndEnterSplitScreenMode"
- runWithFlicker(splitScreenRotationSetup) {
- withTestName { testTag }
- repeat {
- SplitScreenHelper.TEST_REPETITIONS
- }
- transitions {
- splitScreenApp.launchViaIntent()
- setRotation(rotation)
- uiDevice.launchSplitScreen()
- }
- assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943)
- dockedStackDividerIsVisible()
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_90, Surface.ROTATION_270)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-}
\ 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
new file mode 100644
index 0000000..4095b9a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.legacysplitscreen
+
+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.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
+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.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppAndEnterSplitScreen`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class RotateTwoLaunchedAppAndEnterSplitScreen(
+ 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("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,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 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
new file mode 100644
index 0000000..aebf606
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.legacysplitscreen
+
+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.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
+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.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppInSplitScreenMode`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::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()
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, customRotateSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt
deleted file mode 100644
index d8014d3..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt
+++ /dev/null
@@ -1,163 +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.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
-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.wakeUpAndGoToHomeScreen
-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.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-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
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class RotateTwoLaunchedAppTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- private val splitScreenRotationSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- val testSetupRotation = "testSetupRotation"
- withTestName {
- testSetupRotation
- }
- setup {
- test {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- }
- }
- teardown {
- eachRun {
- if (uiDevice.isInSplitScreen()) {
- uiDevice.exitSplitScreen()
- }
- setRotation(Surface.ROTATION_0)
- splitScreenApp.exit()
- secondaryApp.exit()
- }
- }
- }
-
- @Test
- fun testRotateInSplitScreenMode() {
- val testTag = "testRotateInSplitScreenMode"
- runWithFlicker(splitScreenRotationSetup) {
- withTestName { testTag }
- repeat {
- SplitScreenHelper.TEST_REPETITIONS
- }
- transitions {
- secondaryApp.launchViaIntent()
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- splitScreenApp.reopenAppFromOverview()
- setRotation(rotation)
- }
- assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943)
- dockedStackDividerIsVisible()
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- dockedStackSecondaryBoundsIsVisible(
- rotation, secondaryApp.defaultWindowName, 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- @FlakyTest(bugId = 173875043)
- @Test
- fun testRotateAndEnterSplitScreenMode() {
- val testTag = "testRotateAndEnterSplitScreenMode"
- runWithFlicker(splitScreenRotationSetup) {
- withTestName { testTag }
- repeat {
- SplitScreenHelper.TEST_REPETITIONS
- }
- transitions {
- secondaryApp.launchViaIntent()
- splitScreenApp.launchViaIntent()
- setRotation(rotation)
- uiDevice.launchSplitScreen()
- splitScreenApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943)
- dockedStackDividerIsVisible()
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- dockedStackSecondaryBoundsIsVisible(
- rotation, secondaryApp.defaultWindowName, 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_90, Surface.ROTATION_270)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt
deleted file mode 100644
index 01db4ed6..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt
+++ /dev/null
@@ -1,74 +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.legacysplitscreen
-
-import android.support.test.launcherhelper.LauncherStrategyFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isInSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-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.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.NonRotationTestBase
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-
-abstract class SplitScreenTestBase(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- 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
- protected val LIVE_WALLPAPER_PACKAGE_NAME =
- "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
- protected val LETTER_BOX_NAME = "Letterbox"
- protected val TOAST_NAME = "Toast"
-
- protected val transitionSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- }
- }
- teardown {
- eachRun {
- if (uiDevice.isInSplitScreen()) {
- uiDevice.exitSplitScreen()
- }
- splitScreenApp.exit()
- nonResizeableApp.exit()
- }
- }
- assertions {
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- }
- }
-}
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 2ffc338..af62eb9 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
@@ -20,7 +20,6 @@
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.buildTestTag
@@ -53,19 +52,16 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 152738416)
class EnterPipTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0))
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
withTestName { buildTestTag("enterPip", testApp, configuration) }
repeat { configuration.repetitions }
setup {
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 4a6a5b5..a00c5f4 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
@@ -20,7 +20,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.endRotation
@@ -54,10 +53,8 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class PipRotationTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
@@ -65,9 +62,9 @@
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = FixedAppHelper(instrumentation)
val pipApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation,
- listOf(Surface.ROTATION_0, Surface.ROTATION_90))
- .buildRotationTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation,
+ supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)) {
+ configuration ->
withTestName { buildTestTag("PipRotationTest", testApp, configuration) }
repeat { configuration.repetitions }
setup {
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 1502bcd..3e7eb134 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
@@ -20,7 +20,6 @@
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.focusChanges
@@ -54,18 +53,16 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 152738416)
class PipToAppTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
repeat { configuration.repetitions }
setup {
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 df88a2d..5d3bc13 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
@@ -20,7 +20,6 @@
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.focusChanges
@@ -53,18 +52,16 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 152738416)
class PipToHomeTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
repeat { configuration.repetitions }
setup {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 862776e..80ea9b9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -16,11 +16,14 @@
package com.android.wm.shell;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
@@ -48,6 +51,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.sizecompatui.SizeCompatUI;
import org.junit.Before;
import org.junit.Test;
@@ -59,6 +63,9 @@
/**
* Tests for the shell task organizer.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:ShellTaskOrganizerTests
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -68,6 +75,8 @@
private ITaskOrganizerController mTaskOrganizerController;
@Mock
private Context mContext;
+ @Mock
+ private SizeCompatUI mSizeCompatUI;
ShellTaskOrganizer mOrganizer;
private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
@@ -102,7 +111,8 @@
doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList())
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
- mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext));
+ mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
+ mSizeCompatUI));
}
@Test
@@ -257,6 +267,39 @@
assertTrue(mwListener.appeared.contains(task2));
}
+ @Test
+ public void testOnSizeCompatActivityChanged() {
+ final RunningTaskInfo taskInfo1 = createTaskInfo(12, WINDOWING_MODE_FULLSCREEN);
+ taskInfo1.displayId = DEFAULT_DISPLAY;
+ taskInfo1.topActivityToken = mock(IBinder.class);
+ taskInfo1.topActivityInSizeCompat = false;
+ final TrackingTaskListener taskListener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+
+ // sizeCompatActivity is null if top activity is not in size compat.
+ verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ taskInfo1.configuration.windowConfiguration.getBounds(),
+ null /* sizeCompatActivity*/ , taskListener);
+
+ // sizeCompatActivity is non-null if top activity is in size compat.
+ final RunningTaskInfo taskInfo2 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo2.displayId = taskInfo1.displayId;
+ taskInfo2.topActivityToken = taskInfo1.topActivityToken;
+ taskInfo2.topActivityInSizeCompat = true;
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ taskInfo1.configuration.windowConfiguration.getBounds(),
+ taskInfo1.topActivityToken,
+ taskListener);
+
+ mOrganizer.onTaskVanished(taskInfo1);
+ verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ null /* taskConfig */, null /* sizeCompatActivity*/,
+ null /* taskListener */);
+ }
+
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 7c9b9c3..d3a736e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -41,6 +41,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.bubbles.BubbleData.TimeSource;
+import com.android.wm.shell.common.ShellExecutor;
import com.google.common.collect.ImmutableList;
@@ -98,6 +99,8 @@
private PendingIntent mDeleteIntent;
@Mock
private BubbleLogger mBubbleLogger;
+ @Mock
+ private ShellExecutor mMainExecutor;
@Captor
private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@@ -124,21 +127,31 @@
mock(NotificationListenerService.Ranking.class);
when(ranking.visuallyInterruptive()).thenReturn(true);
mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d", ranking);
- mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null);
+ mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null,
+ mMainExecutor);
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d", null);
- mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null);
+ mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null,
+ mMainExecutor);
- mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener);
- mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener, mPendingIntentCanceledListener);
- mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener, mPendingIntentCanceledListener);
- mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener, mPendingIntentCanceledListener);
- mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener, mPendingIntentCanceledListener);
- mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener);
- mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener,
+ mMainExecutor);
+ mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener, mPendingIntentCanceledListener,
+ mMainExecutor);
+ mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener, mPendingIntentCanceledListener,
+ mMainExecutor);
+ mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener, mPendingIntentCanceledListener,
+ mMainExecutor);
+ mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener, mPendingIntentCanceledListener,
+ mMainExecutor);
+ mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener,
+ mMainExecutor);
+ mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener,
+ mMainExecutor);
TestableBubblePositioner positioner = new TestableBubblePositioner(mContext,
mock(WindowManager.class));
- mBubbleData = new BubbleData(getContext(), mBubbleLogger, positioner);
+ mBubbleData = new BubbleData(getContext(), mBubbleLogger, positioner,
+ mMainExecutor);
// Used by BubbleData to set lastAccessedTime
when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
@@ -796,7 +809,7 @@
assertWithMessage("addedBubble").that(update.addedBubble).isEqualTo(expected);
}
- private void assertBubbleRemoved(Bubble expected, @BubbleController.DismissReason int reason) {
+ private void assertBubbleRemoved(Bubble expected, @Bubbles.DismissReason int reason) {
BubbleData.Update update = mUpdateCaptor.getValue();
assertWithMessage("removedBubbles").that(update.removedBubbles)
.isEqualTo(ImmutableList.of(Pair.create(expected, reason)));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
index bde04b6..fc828b3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
@@ -39,6 +39,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
@@ -54,6 +55,8 @@
private Notification mNotif;
@Mock
private StatusBarNotification mSbn;
+ @Mock
+ private ShellExecutor mMainExecutor;
private BubbleEntry mBubbleEntry;
private Bundle mExtras;
@@ -78,7 +81,7 @@
when(mNotif.getBubbleMetadata()).thenReturn(metadata);
when(mSbn.getKey()).thenReturn("mock");
mBubbleEntry = new BubbleEntry(mSbn, null, true, false, false, false);
- mBubble = new Bubble(mBubbleEntry, mSuppressionListener, null);
+ mBubble = new Bubble(mBubbleEntry, mSuppressionListener, null, mMainExecutor);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 79bdaf4..2572106 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -29,6 +29,10 @@
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -86,11 +90,9 @@
@Mock
private ActivityTaskManager mActivityTaskManager;
+ // Both the split-screen and start interface.
@Mock
- private SplitScreen mSplitScreen;
-
- @Mock
- private DragAndDropPolicy.Starter mStarter;
+ private SplitScreen mSplitScreenStarter;
private DisplayLayout mLandscapeDisplayLayout;
private DisplayLayout mPortraitDisplayLayout;
@@ -126,7 +128,7 @@
mInsets = Insets.of(0, 0, 0, 0);
mPolicy = new DragAndDropPolicy(
- mContext, mActivityTaskManager, mSplitScreen, mStarter);
+ mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter);
mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -191,7 +193,7 @@
}
private void setInSplitScreen(boolean inSplitscreen) {
- doReturn(inSplitscreen).when(mSplitScreen).isSplitScreenVisible();
+ doReturn(inSplitscreen).when(mSplitScreenStarter).isSplitScreenVisible();
}
@Test
@@ -202,7 +204,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
}
@Test
@@ -213,12 +216,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).exitSplitScreen();
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
@@ -229,12 +233,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).exitSplitScreen();
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
@@ -245,7 +250,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
}
@Test
@@ -256,7 +262,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
}
@Test
@@ -268,12 +275,14 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
@@ -285,12 +294,14 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
index 0d4d126..35656bd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
@@ -47,17 +47,17 @@
private PipTouchState mTouchState;
private CountDownLatch mDoubleTapCallbackTriggeredLatch;
private CountDownLatch mHoverExitCallbackTriggeredLatch;
- private TestShellExecutor mShellMainExecutor;
+ private TestShellExecutor mMainExecutor;
@Before
public void setUp() throws Exception {
- mShellMainExecutor = new TestShellExecutor();
+ mMainExecutor = new TestShellExecutor();
mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1);
mHoverExitCallbackTriggeredLatch = new CountDownLatch(1);
mTouchState = new PipTouchState(ViewConfiguration.get(getContext()),
mDoubleTapCallbackTriggeredLatch::countDown,
mHoverExitCallbackTriggeredLatch::countDown,
- mShellMainExecutor);
+ mMainExecutor);
assertFalse(mTouchState.isDoubleTap());
assertFalse(mTouchState.isWaitingForDoubleTap());
}
@@ -87,7 +87,7 @@
assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == 10);
mTouchState.scheduleDoubleTapTimeoutCallback();
- mShellMainExecutor.flushAll();
+ mMainExecutor.flushAll();
assertTrue(mDoubleTapCallbackTriggeredLatch.getCount() == 0);
}
@@ -122,7 +122,7 @@
@Test
public void testHoverExitTimeout_timeoutCallbackCalled() throws Exception {
mTouchState.scheduleHoverExitTimeoutCallback();
- mShellMainExecutor.flushAll();
+ mMainExecutor.flushAll();
assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 0);
}
@@ -137,7 +137,7 @@
mTouchState.scheduleHoverExitTimeoutCallback();
mTouchState.onTouchEvent(createMotionEvent(ACTION_BUTTON_PRESS, SystemClock.uptimeMillis(),
0, 0));
- mShellMainExecutor.flushAll();
+ mMainExecutor.flushAll();
assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
new file mode 100644
index 0000000..98f01ff
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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 org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link SizeCompatUIController}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:SizeCompatUIControllerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SizeCompatUIControllerTest extends ShellTestCase {
+ private static final int DISPLAY_ID = 0;
+
+ private final TestShellExecutor mShellMainExecutor = new TestShellExecutor();
+
+ private SizeCompatUIController mController;
+ private @Mock DisplayController mMockDisplayController;
+ private @Mock DisplayImeController mMockImeController;
+ private @Mock SizeCompatRestartButton mMockButton;
+ private @Mock IBinder mMockActivityToken;
+ private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(true).when(mMockButton).show();
+
+ mController = new SizeCompatUIController(mContext, mMockDisplayController,
+ mMockImeController, mShellMainExecutor) {
+ @Override
+ SizeCompatRestartButton createRestartButton(Context context, int displayId) {
+ return mMockButton;
+ }
+ };
+ }
+
+ @Test
+ public void testListenerRegistered() {
+ verify(mMockDisplayController).addDisplayWindowListener(mController);
+ verify(mMockImeController).addPositionProcessor(mController);
+ }
+
+ @Test
+ public void testOnSizeCompatInfoChanged() {
+ final int taskId = 12;
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+
+ // Verify that the restart button is added with non-null size compat activity.
+ mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ mMockActivityToken, mMockTaskListener);
+ mShellMainExecutor.flushAll();
+
+ verify(mMockButton).show();
+ verify(mMockButton).updateLastTargetActivity(eq(mMockActivityToken));
+
+ // Verify that the restart button is removed with null size compat activity.
+ mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, null, null, null);
+
+ mShellMainExecutor.flushAll();
+ verify(mMockButton).remove();
+ }
+
+ @Test
+ public void testChangeButtonVisibilityOnImeShowHide() {
+ final int taskId = 12;
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+ mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ mMockActivityToken, mMockTaskListener);
+ mShellMainExecutor.flushAll();
+
+ // Verify that the restart button is hidden when IME is visible.
+ doReturn(View.VISIBLE).when(mMockButton).getVisibility();
+ mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
+
+ verify(mMockButton).setVisibility(eq(View.GONE));
+
+ // Verify that the restart button is visible when IME is hidden.
+ doReturn(View.GONE).when(mMockButton).getVisibility();
+ mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
+
+ verify(mMockButton).setVisibility(eq(View.VISIBLE));
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 168e0df..d2d1812 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -19,7 +19,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -71,7 +71,7 @@
public void testMoveToSideStage() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
- mStageCoordinator.moveToSideStage(task, SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
+ mStageCoordinator.moveToSideStage(task, STAGE_POSITION_BOTTOM_OR_RIGHT);
verify(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class));
verify(mSideStage).addTask(eq(task), any(Rect.class),
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index b54f7d8..4b4284a 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -41,6 +41,7 @@
"AssetDir.cpp",
"AssetManager.cpp",
"AssetManager2.cpp",
+ "AssetsProvider.cpp",
"AttributeResolution.cpp",
"ChunkIterator.cpp",
"ConfigDescription.cpp",
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 011a0de..ca5981c0 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -16,533 +16,145 @@
#include "androidfw/ApkAssets.h"
-#include <algorithm>
-
#include "android-base/errors.h"
-#include "android-base/file.h"
#include "android-base/logging.h"
-#include "android-base/stringprintf.h"
-#include "android-base/unique_fd.h"
-#include "android-base/utf8.h"
-#include "utils/Compat.h"
-#include "ziparchive/zip_archive.h"
-
-#include "androidfw/Asset.h"
-#include "androidfw/Idmap.h"
-#include "androidfw/misc.h"
-#include "androidfw/Util.h"
namespace android {
using base::SystemErrorCodeToString;
using base::unique_fd;
-static const std::string kResourcesArsc("resources.arsc");
+constexpr const char* kResourcesArsc = "resources.arsc";
-ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
- std::string path,
- time_t last_mod_time,
- package_property_t property_flags)
- : assets_provider_(std::move(assets_provider)),
- path_(std::move(path)),
- last_mod_time_(last_mod_time),
- property_flags_(property_flags) {
+ApkAssets::ApkAssets(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<LoadedArsc> loaded_arsc,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap)
+ : resources_asset_(std::move(resources_asset)),
+ loaded_arsc_(std::move(loaded_arsc)),
+ assets_provider_(std::move(assets)),
+ property_flags_(property_flags),
+ idmap_asset_(std::move(idmap_asset)),
+ loaded_idmap_(std::move(loaded_idmap)) {}
+
+std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path, package_property_t flags) {
+ return Load(ZipAssetsProvider::Create(path), flags);
}
-// Provides asset files from a zip file.
-class ZipAssetsProvider : public AssetsProvider {
- public:
- ~ZipAssetsProvider() override = default;
-
- static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
- ::ZipArchiveHandle unmanaged_handle;
- const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
- if (result != 0) {
- LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
- ::CloseArchive(unmanaged_handle);
- return {};
- }
-
- return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle));
- }
-
- static std::unique_ptr<const AssetsProvider> Create(
- unique_fd fd, const std::string& friendly_name, const off64_t offset = 0,
- const off64_t length = ApkAssets::kUnknownLength) {
-
- ::ZipArchiveHandle unmanaged_handle;
- const int32_t result = (length == ApkAssets::kUnknownLength)
- ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle)
- : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length,
- offset);
-
- if (result != 0) {
- LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
- << " and length " << length << ": " << ::ErrorCodeString(result);
- ::CloseArchive(unmanaged_handle);
- return {};
- }
-
- return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name,
- unmanaged_handle));
- }
-
- // Iterate over all files and directories within the zip. The order of iteration is not
- // guaranteed to be the same as the order of elements in the central directory but is stable for a
- // given zip file.
- bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override {
- // If this is a resource loader from an .arsc, there will be no zip handle
- if (zip_handle_ == nullptr) {
- return false;
- }
-
- std::string root_path_full = root_path;
- if (root_path_full.back() != '/') {
- root_path_full += '/';
- }
-
- void* cookie;
- if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
- return false;
- }
-
- std::string name;
- ::ZipEntry entry{};
-
- // We need to hold back directories because many paths will contain them and we want to only
- // surface one.
- std::set<std::string> dirs{};
-
- int32_t result;
- while ((result = ::Next(cookie, &entry, &name)) == 0) {
- StringPiece full_file_path(name);
- StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
-
- if (!leaf_file_path.empty()) {
- auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
- if (iter != leaf_file_path.end()) {
- std::string dir =
- leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
- dirs.insert(std::move(dir));
- } else {
- f(leaf_file_path, kFileTypeRegular);
- }
- }
- }
- ::EndIteration(cookie);
-
- // Now present the unique directories.
- for (const std::string& dir : dirs) {
- f(dir, kFileTypeDirectory);
- }
-
- // -1 is end of iteration, anything else is an error.
- return result == -1;
- }
-
- protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
- if (file_exists) {
- *file_exists = false;
- }
-
- ::ZipEntry entry;
- int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
- if (result != 0) {
- return {};
- }
-
- if (file_exists) {
- *file_exists = true;
- }
-
- const int fd = ::GetFileDescriptor(zip_handle_.get());
- const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
- incfs::IncFsFileMap asset_map;
- if (entry.method == kCompressDeflated) {
- if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
-
- std::unique_ptr<Asset> asset =
- Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- return asset;
- }
-
- if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
-
- unique_fd ufd;
- if (!GetPath()) {
- // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
- // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
- // to create new file descriptors.
- ufd = unique_fd(dup(fd));
- if (!ufd.ok()) {
- LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- }
-
- auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- return asset;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider);
-
- explicit ZipAssetsProvider(std::string path,
- std::string friendly_name,
- ZipArchiveHandle unmanaged_handle)
- : zip_handle_(unmanaged_handle, ::CloseArchive),
- path_(std::move(path)),
- friendly_name_(std::move(friendly_name)) { }
-
- const char* GetPath() const {
- return path_.empty() ? nullptr : path_.c_str();
- }
-
- using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
- ZipArchivePtr zip_handle_;
- std::string path_;
- std::string friendly_name_;
-};
-
-class DirectoryAssetsProvider : AssetsProvider {
- public:
- ~DirectoryAssetsProvider() override = default;
-
- static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
- struct stat sb{};
- const int result = stat(path.c_str(), &sb);
- if (result == -1) {
- LOG(ERROR) << "Failed to find directory '" << path << "'.";
- return nullptr;
- }
-
- if (!S_ISDIR(sb.st_mode)) {
- LOG(ERROR) << "Path '" << path << "' is not a directory.";
- return nullptr;
- }
-
- return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path));
- }
-
- protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override {
- const std::string resolved_path = ResolvePath(path);
- if (file_exists) {
- struct stat sb{};
- const int result = stat(resolved_path.c_str(), &sb);
- *file_exists = result != -1 && S_ISREG(sb.st_mode);
- }
-
- return ApkAssets::CreateAssetFromFile(resolved_path);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider);
-
- explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { }
-
- inline std::string ResolvePath(const std::string& path) const {
- return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str());
- }
-
- const std::string path_;
-};
-
-// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty.
-class EmptyAssetsProvider : public AssetsProvider {
- public:
- EmptyAssetsProvider() = default;
- ~EmptyAssetsProvider() override = default;
-
- protected:
- std::unique_ptr<Asset> OpenInternal(const std::string& /*path */,
- Asset::AccessMode /* mode */,
- bool* file_exists) const override {
- if (file_exists) {
- *file_exists = false;
- }
- return nullptr;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider);
-};
-
-// AssetProvider implementation
-class MultiAssetsProvider : public AssetsProvider {
- public:
- ~MultiAssetsProvider() override = default;
-
- static std::unique_ptr<const AssetsProvider> Create(
- std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) {
- CHECK(parent != nullptr) << "parent provider must not be null";
- return (!child) ? std::move(parent)
- : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider(
- std::move(child), std::move(parent)));
- }
-
- bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override {
- // TODO: Only call the function once for files defined in the parent and child
- return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f);
- }
-
- protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
- auto asset = child_->Open(path, mode, file_exists);
- return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider);
-
- MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child,
- std::unique_ptr<const AssetsProvider> parent)
- : child_(std::move(child)), parent_(std::move(parent)) { }
-
- std::unique_ptr<const AssetsProvider> child_;
- std::unique_ptr<const AssetsProvider> parent_;
-};
-
-// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the
-// file.
-std::unique_ptr<const ApkAssets> ApkAssets::Load(
- const std::string& path, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset) {
- auto assets = ZipAssetsProvider::Create(path);
- return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
- : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::LoadFromFd(base::unique_fd fd,
+ const std::string& debug_name,
+ package_property_t flags,
+ off64_t offset,
+ off64_t len) {
+ return Load(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags);
}
-// Opens the archive using the file file descriptor with the specified file offset and read length.
-// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file.
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(
- unique_fd fd, const std::string& friendly_name, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
- const off64_t length) {
- CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
- CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
- << kUnknownLength;
-
- auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length);
- return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset))
- : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::Load(std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags) {
+ return LoadImpl(std::move(assets), flags, nullptr /* idmap_asset */, nullptr /* loaded_idmap */);
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(
- const std::string& path, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset) {
-
- auto assets = CreateAssetFromFile(path);
- return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset))
- : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::LoadTable(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags) {
+ if (resources_asset == nullptr) {
+ return {};
+ }
+ return LoadImpl(std::move(resources_asset), std::move(assets), flags, nullptr /* idmap_asset */,
+ nullptr /* loaded_idmap */);
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(
- unique_fd fd, const std::string& friendly_name, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
- const off64_t length) {
-
- auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
- return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags,
- std::move(override_asset))
- : nullptr;
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
- const package_property_t flags) {
+std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
+ package_property_t flags) {
CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
- std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
+ auto idmap_asset = AssetsProvider::CreateAssetFromFile(idmap_path);
if (idmap_asset == nullptr) {
+ LOG(ERROR) << "failed to read IDMAP " << idmap_path;
return {};
}
- const StringPiece idmap_data(
- reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
- static_cast<size_t>(idmap_asset->getLength()));
- std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
+ StringPiece idmap_data(reinterpret_cast<const char*>(idmap_asset->getBuffer(true /* aligned */)),
+ static_cast<size_t>(idmap_asset->getLength()));
+ auto loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
if (loaded_idmap == nullptr) {
LOG(ERROR) << "failed to load IDMAP " << idmap_path;
return {};
}
-
- auto overlay_path = std::string(loaded_idmap->OverlayApkPath());
- auto assets = ZipAssetsProvider::Create(overlay_path);
- return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY,
- nullptr /* override_asset */, std::move(idmap_asset),
- std::move(loaded_idmap))
- : nullptr;
-}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(
- const std::string& path, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset) {
-
- auto assets = DirectoryAssetsProvider::Create(path);
- return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
- : nullptr;
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(
- const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) {
-
- auto assets = (override_asset) ? std::move(override_asset)
- : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider());
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */,
- -1 /* last_mod-time */, flags));
- loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
- // Need to force a move for mingw32.
- return std::move(loaded_apk);
-}
-
-std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
- unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
- if (!fd.ok()) {
- LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
+ const std::string overlay_path(loaded_idmap->OverlayApkPath());
+ auto overlay_assets = ZipAssetsProvider::Create(overlay_path);
+ if (overlay_assets == nullptr) {
return {};
}
- return CreateAssetFromFd(std::move(fd), path.c_str());
+ return LoadImpl(std::move(overlay_assets), flags | PROPERTY_OVERLAY, std::move(idmap_asset),
+ std::move(loaded_idmap));
}
-std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
- const char* path,
- off64_t offset,
- off64_t length) {
- CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
- CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
- << kUnknownLength;
- if (length == kUnknownLength) {
- length = lseek64(fd, 0, SEEK_END);
- if (length < 0) {
- LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
- << SystemErrorCodeToString(errno);
- return {};
- }
- }
-
- incfs::IncFsFileMap file_map;
- if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
- LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
- << SystemErrorCodeToString(errno);
+std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap) {
+ if (assets == nullptr) {
return {};
}
- // If `path` is set, do not pass ownership of the `fd` to the new Asset since
- // Asset::openFileDescriptor can use `path` to create new file descriptors.
- return Asset::createFromUncompressedMap(std::move(file_map),
- Asset::AccessMode::ACCESS_RANDOM,
- (path) ? base::unique_fd(-1) : std::move(fd));
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
- std::unique_ptr<const AssetsProvider> assets, const std::string& path,
- package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets,
- std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) {
-
- const time_t last_mod_time = getFileModDate(path.c_str());
-
// Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
bool resources_asset_exists = false;
- auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
- &resources_asset_exists);
-
- assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets));
-
- // Wrap the handle in a unique_ptr so it gets automatically closed.
- std::unique_ptr<ApkAssets>
- loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
-
- if (!resources_asset_exists) {
- loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
- return std::move(loaded_apk);
- }
-
- loaded_apk->resources_asset_ = std::move(resources_asset_);
- if (!loaded_apk->resources_asset_) {
- LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
+ auto resources_asset = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
+ &resources_asset_exists);
+ if (resources_asset == nullptr && resources_asset_exists) {
+ LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << assets->GetDebugName()
+ << "'.";
return {};
}
- // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
- loaded_apk->idmap_asset_ = std::move(idmap_asset);
- loaded_apk->loaded_idmap_ = std::move(idmap);
-
- const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
- const size_t length = loaded_apk->resources_asset_->getLength();
- if (!data || length == 0) {
- LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'.";
- return {};
- }
-
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(),
- property_flags);
- if (!loaded_apk->loaded_arsc_) {
- LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
- return {};
- }
-
- // Need to force a move for mingw32.
- return std::move(loaded_apk);
+ return LoadImpl(std::move(resources_asset), std::move(assets), property_flags,
+ std::move(idmap_asset), std::move(loaded_idmap));
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(
- std::unique_ptr<Asset> resources_asset, const std::string& path,
- package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) {
-
- const time_t last_mod_time = getFileModDate(path.c_str());
-
- auto assets = (override_assets) ? std::move(override_assets)
- : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider());
-
- std::unique_ptr<ApkAssets> loaded_apk(
- new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
- loaded_apk->resources_asset_ = std::move(resources_asset);
-
- const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
- const size_t length = loaded_apk->resources_asset_->getLength();
- if (!data || length == 0) {
- LOG(ERROR) << "Failed to read resources table data in '" << path << "'.";
+std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap) {
+ if (assets == nullptr ) {
return {};
}
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */,
- property_flags);
- if (loaded_apk->loaded_arsc_ == nullptr) {
- LOG(ERROR) << "Failed to read resources table in '" << path << "'.";
+ std::unique_ptr<LoadedArsc> loaded_arsc;
+ if (resources_asset != nullptr) {
+ const auto data = resources_asset->getIncFsBuffer(true /* aligned */);
+ const size_t length = resources_asset->getLength();
+ if (!data || length == 0) {
+ LOG(ERROR) << "Failed to read resources table in APK '" << assets->GetDebugName() << "'.";
+ return {};
+ }
+ loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags);
+ } else {
+ loaded_arsc = LoadedArsc::CreateEmpty();
+ }
+
+ if (loaded_arsc == nullptr) {
+ LOG(ERROR) << "Failed to load resources table in APK '" << assets->GetDebugName() << "'.";
return {};
}
- // Need to force a move for mingw32.
- return std::move(loaded_apk);
+ return std::unique_ptr<ApkAssets>(new ApkAssets(std::move(resources_asset),
+ std::move(loaded_arsc), std::move(assets),
+ property_flags, std::move(idmap_asset),
+ std::move(loaded_idmap)));
+}
+
+const std::string& ApkAssets::GetPath() const {
+ return assets_provider_->GetDebugName();
}
bool ApkAssets::IsUpToDate() const {
- if (IsLoader()) {
- // Loaders are invalidated by the app, not the system, so assume they are up to date.
- return true;
- }
- return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) &&
- last_mod_time_ == getFileModDate(path_.c_str());
+ // Loaders are invalidated by the app, not the system, so assume they are up to date.
+ return IsLoader() || ((!loaded_idmap_ || loaded_idmap_->IsUpToDate())
+ && assets_provider_->IsUpToDate());
}
-
} // namespace android
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
new file mode 100644
index 0000000..23cacf8
--- /dev/null
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -0,0 +1,398 @@
+/*
+ * 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 "androidfw/AssetsProvider.h"
+
+#include <sys/stat.h>
+
+#include <android-base/errors.h>
+#include <android-base/stringprintf.h>
+#include <android-base/utf8.h>
+#include <ziparchive/zip_archive.h>
+
+namespace android {
+namespace {
+constexpr const char* kEmptyDebugString = "<empty>";
+} // namespace
+
+std::unique_ptr<Asset> AssetsProvider::Open(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const {
+ return OpenInternal(path, mode, file_exists);
+}
+
+std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFile(const std::string& path) {
+ base::unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ if (!fd.ok()) {
+ LOG(ERROR) << "Failed to open file '" << path << "': " << base::SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ return CreateAssetFromFd(std::move(fd), path.c_str());
+}
+
+std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset,
+ off64_t length) {
+ CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
+ CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
+ << kUnknownLength;
+ if (length == kUnknownLength) {
+ length = lseek64(fd, 0, SEEK_END);
+ if (length < 0) {
+ LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
+ << base::SystemErrorCodeToString(errno);
+ return {};
+ }
+ }
+
+ incfs::IncFsFileMap file_map;
+ if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
+ LOG(ERROR) << "Failed to mmap file '" << ((path != nullptr) ? path : "anon") << "': "
+ << base::SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ // If `path` is set, do not pass ownership of the `fd` to the new Asset since
+ // Asset::openFileDescriptor can use `path` to create new file descriptors.
+ return Asset::createFromUncompressedMap(std::move(file_map),
+ Asset::AccessMode::ACCESS_RANDOM,
+ (path != nullptr) ? base::unique_fd(-1) : std::move(fd));
+}
+
+ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path)
+ : value_(std::forward<std::string>(value)), is_path_(is_path) {}
+
+const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const {
+ return is_path_ ? &value_ : nullptr;
+}
+
+const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
+ return value_;
+}
+
+ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path,
+ time_t last_mod_time)
+ : zip_handle_(handle, ::CloseArchive),
+ name_(std::forward<PathOrDebugName>(path)),
+ last_mod_time_(last_mod_time) {}
+
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) {
+ ZipArchiveHandle handle;
+ if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
+ CloseArchive(handle);
+ return {};
+ }
+
+ struct stat sb{.st_mtime = -1};
+ if (stat(path.c_str(), &sb) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to stat file '" << path << "': "
+ << base::SystemErrorCodeToString(errno);
+ }
+
+ return std::unique_ptr<ZipAssetsProvider>(
+ new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
+ true /* is_path */}, sb.st_mtime));
+}
+
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
+ std::string friendly_name,
+ off64_t offset,
+ off64_t len) {
+ ZipArchiveHandle handle;
+ const int released_fd = fd.release();
+ const int32_t result = (len == AssetsProvider::kUnknownLength)
+ ? ::OpenArchiveFd(released_fd, friendly_name.c_str(), &handle)
+ : ::OpenArchiveFdRange(released_fd, friendly_name.c_str(), &handle, len, offset);
+
+ if (result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
+ << " and length " << len << ": " << ::ErrorCodeString(result);
+ CloseArchive(handle);
+ return {};
+ }
+
+ struct stat sb{.st_mtime = -1};
+ if (fstat(released_fd, &sb) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': "
+ << base::SystemErrorCodeToString(errno);
+ }
+
+ return std::unique_ptr<ZipAssetsProvider>(
+ new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
+ false /* is_path */}, sb.st_mtime));
+}
+
+std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const {
+ if (file_exists != nullptr) {
+ *file_exists = false;
+ }
+
+ ZipEntry entry;
+ if (FindEntry(zip_handle_.get(), path, &entry) != 0) {
+ return {};
+ }
+
+ if (file_exists != nullptr) {
+ *file_exists = true;
+ }
+
+ const int fd = GetFileDescriptor(zip_handle_.get());
+ const off64_t fd_offset = GetFileDescriptorOffset(zip_handle_.get());
+ incfs::IncFsFileMap asset_map;
+ if (entry.method == kCompressDeflated) {
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length,
+ name_.GetDebugName().c_str())) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName()
+ << "'";
+ return {};
+ }
+
+ std::unique_ptr<Asset> asset =
+ Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << name_.GetDebugName()
+ << "'";
+ return {};
+ }
+ return asset;
+ }
+
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length,
+ name_.GetDebugName().c_str())) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
+ return {};
+ }
+
+ base::unique_fd ufd;
+ if (name_.GetPath() == nullptr) {
+ // If the zip name does not represent a path, create a new `fd` for the new Asset to own in
+ // order to create new file descriptors using Asset::openFileDescriptor. If the zip name is a
+ // path, it will be used to create new file descriptors.
+ ufd = base::unique_fd(dup(fd));
+ if (!ufd.ok()) {
+ LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << name_.GetDebugName() << "'";
+ return {};
+ }
+ }
+
+ auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
+ return {};
+ }
+ return asset;
+}
+
+bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f)
+ const {
+ std::string root_path_full = root_path;
+ if (root_path_full.back() != '/') {
+ root_path_full += '/';
+ }
+
+ void* cookie;
+ if (StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
+ return false;
+ }
+
+ std::string name;
+ ::ZipEntry entry{};
+
+ // We need to hold back directories because many paths will contain them and we want to only
+ // surface one.
+ std::set<std::string> dirs{};
+
+ int32_t result;
+ while ((result = Next(cookie, &entry, &name)) == 0) {
+ StringPiece full_file_path(name);
+ StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
+
+ if (!leaf_file_path.empty()) {
+ auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
+ if (iter != leaf_file_path.end()) {
+ std::string dir =
+ leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+ dirs.insert(std::move(dir));
+ } else {
+ f(leaf_file_path, kFileTypeRegular);
+ }
+ }
+ }
+ EndIteration(cookie);
+
+ // Now present the unique directories.
+ for (const std::string& dir : dirs) {
+ f(dir, kFileTypeDirectory);
+ }
+
+ // -1 is end of iteration, anything else is an error.
+ return result == -1;
+}
+
+const std::string& ZipAssetsProvider::GetDebugName() const {
+ return name_.GetDebugName();
+}
+
+bool ZipAssetsProvider::IsUpToDate() const {
+ struct stat sb{};
+ if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
+ // If fstat fails on the zip archive, return true so the zip archive the resource system does
+ // attempt to refresh the ApkAsset.
+ return true;
+ }
+ return last_mod_time_ == sb.st_mtime;
+}
+
+DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
+ : dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}
+
+std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
+ struct stat sb{};
+ const int result = stat(path.c_str(), &sb);
+ if (result == -1) {
+ LOG(ERROR) << "Failed to find directory '" << path << "'.";
+ return nullptr;
+ }
+
+ if (!S_ISDIR(sb.st_mode)) {
+ LOG(ERROR) << "Path '" << path << "' is not a directory.";
+ return nullptr;
+ }
+
+ if (path[path.size() - 1] != OS_PATH_SEPARATOR) {
+ path += OS_PATH_SEPARATOR;
+ }
+
+ return std::unique_ptr<DirectoryAssetsProvider>(new DirectoryAssetsProvider(std::move(path),
+ sb.st_mtime));
+}
+
+std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
+ Asset::AccessMode /* mode */,
+ bool* file_exists) const {
+ const std::string resolved_path = dir_ + path;
+ if (file_exists != nullptr) {
+ struct stat sb{};
+ *file_exists = (stat(resolved_path.c_str(), &sb) != -1) && S_ISREG(sb.st_mode);
+ }
+
+ return CreateAssetFromFile(resolved_path);
+}
+
+bool DirectoryAssetsProvider::ForEachFile(
+ const std::string& /* root_path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */)
+ const {
+ return true;
+}
+
+const std::string& DirectoryAssetsProvider::GetDebugName() const {
+ return dir_;
+}
+
+bool DirectoryAssetsProvider::IsUpToDate() const {
+ struct stat sb{};
+ if (stat(dir_.c_str(), &sb) < 0) {
+ // If stat fails on the zip archive, return true so the zip archive the resource system does
+ // attempt to refresh the ApkAsset.
+ return true;
+ }
+ return last_mod_time_ == sb.st_mtime;
+}
+
+MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
+ std::unique_ptr<AssetsProvider>&& secondary)
+ : primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
+ secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) {
+ if (primary_->GetDebugName() == kEmptyDebugString) {
+ debug_name_ = secondary_->GetDebugName();
+ } else if (secondary_->GetDebugName() == kEmptyDebugString) {
+ debug_name_ = primary_->GetDebugName();
+ } else {
+ debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
+ }
+}
+
+std::unique_ptr<AssetsProvider> MultiAssetsProvider::Create(
+ std::unique_ptr<AssetsProvider>&& primary, std::unique_ptr<AssetsProvider>&& secondary) {
+ if (primary == nullptr || secondary == nullptr) {
+ return nullptr;
+ }
+ return std::unique_ptr<MultiAssetsProvider>(new MultiAssetsProvider(std::move(primary),
+ std::move(secondary)));
+}
+
+std::unique_ptr<Asset> MultiAssetsProvider::OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const {
+ auto asset = primary_->Open(path, mode, file_exists);
+ return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists);
+}
+
+bool MultiAssetsProvider::ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f)
+ const {
+ return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f);
+}
+
+const std::string& MultiAssetsProvider::GetDebugName() const {
+ return debug_name_;
+}
+
+bool MultiAssetsProvider::IsUpToDate() const {
+ return primary_->IsUpToDate() && secondary_->IsUpToDate();
+}
+
+std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create() {
+ return std::make_unique<EmptyAssetsProvider>();
+}
+
+std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* path */,
+ Asset::AccessMode /* mode */,
+ bool* file_exists) const {
+ if (file_exists) {
+ *file_exists = false;
+ }
+ return nullptr;
+}
+
+bool EmptyAssetsProvider::ForEachFile(
+ const std::string& /* root_path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ return true;
+}
+
+const std::string& EmptyAssetsProvider::GetDebugName() const {
+ const static std::string kEmpty = kEmptyDebugString;
+ return kEmpty;
+}
+
+bool EmptyAssetsProvider::IsUpToDate() const {
+ return true;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index adb383f95..f216f55 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -257,8 +257,8 @@
target_apk_path_(target_apk_path),
idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
-std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data) {
+std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
+ const StringPiece& idmap_data) {
ATRACE_CALL();
size_t data_size = idmap_data.size();
auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 996b424..2a70f0d 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -767,10 +767,10 @@
return true;
}
-std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
- const size_t length,
- const LoadedIdmap* loaded_idmap,
- const package_property_t property_flags) {
+std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
+ const size_t length,
+ const LoadedIdmap* loaded_idmap,
+ const package_property_t property_flags) {
ATRACE_NAME("LoadedArsc::Load");
// Not using make_unique because the constructor is private.
@@ -799,11 +799,10 @@
}
}
- // Need to force a move for mingw32.
- return std::move(loaded_arsc);
+ return loaded_arsc;
}
-std::unique_ptr<const LoadedArsc> LoadedArsc::CreateEmpty() {
+std::unique_ptr<LoadedArsc> LoadedArsc::CreateEmpty() {
return std::unique_ptr<LoadedArsc>(new LoadedArsc());
}
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index e57490a..d0019ed 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -24,104 +24,49 @@
#include "android-base/unique_fd.h"
#include "androidfw/Asset.h"
+#include "androidfw/AssetsProvider.h"
#include "androidfw/Idmap.h"
#include "androidfw/LoadedArsc.h"
#include "androidfw/misc.h"
-struct ZipArchive;
-typedef ZipArchive* ZipArchiveHandle;
-
namespace android {
-class LoadedIdmap;
-
-// Interface for retrieving assets provided by an ApkAssets.
-class AssetsProvider {
- public:
- virtual ~AssetsProvider() = default;
-
- // Opens a file for reading.
- std::unique_ptr<Asset> Open(const std::string& path,
- Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
- bool* file_exists = nullptr) const {
- return OpenInternal(path, mode, file_exists);
- }
-
- // Iterate over all files and directories provided by the zip. The order of iteration is stable.
- virtual bool ForEachFile(const std::string& /* path */,
- const std::function<void(const StringPiece&, FileType)>& /* f */) const {
- return true;
- }
-
- protected:
- AssetsProvider() = default;
-
- virtual std::unique_ptr<Asset> OpenInternal(const std::string& path,
- Asset::AccessMode mode,
- bool* file_exists) const = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AssetsProvider);
-};
-
-class ZipAssetsProvider;
-
// Holds an APK.
class ApkAssets {
public:
- // This means the data extends to the end of the file.
- static constexpr off64_t kUnknownLength = -1;
- // Creates an ApkAssets.
- // If `system` is true, the package is marked as a system package, and allows some functions to
- // filter out this package when computing what configurations/resources are available.
- static std::unique_ptr<const ApkAssets> Load(
- const std::string& path, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+ // Creates an ApkAssets from a path on device.
+ static std::unique_ptr<ApkAssets> Load(const std::string& path,
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the given file descriptor, and takes ownership of the file
- // descriptor. The `friendly_name` is some name that will be used to identify the source of
- // this ApkAssets in log messages and other debug scenarios.
- // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read
- // using the `offset` into the file descriptor and will be `length` bytes long.
- static std::unique_ptr<const ApkAssets> LoadFromFd(
- base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
- off64_t length = kUnknownLength);
+ // Creates an ApkAssets from an open file descriptor.
+ static std::unique_ptr<ApkAssets> LoadFromFd(base::unique_fd fd,
+ const std::string& debug_name,
+ package_property_t flags = 0U,
+ off64_t offset = 0,
+ off64_t len = AssetsProvider::kUnknownLength);
- // Creates an ApkAssets from the given path which points to a resources.arsc.
- static std::unique_ptr<const ApkAssets> LoadTable(
- const std::string& path, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+ // Creates an ApkAssets from an AssetProvider.
+ // The ApkAssets will take care of destroying the AssetsProvider when it is destroyed.
+ static std::unique_ptr<ApkAssets> Load(std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and
- // takes ownership of the file descriptor.
- // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read
- // using the `offset` into the file descriptor and will be `length` bytes long.
- static std::unique_ptr<const ApkAssets> LoadTableFromFd(
- base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
- off64_t length = kUnknownLength);
+ // Creates an ApkAssets from the given asset file representing a resources.arsc.
+ static std::unique_ptr<ApkAssets> LoadTable(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags = 0U);
// Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay
// data.
- static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path,
- package_property_t flags = 0U);
+ static std::unique_ptr<ApkAssets> LoadOverlay(const std::string& idmap_path,
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the directory path. File-based resources are read within the
- // directory as if the directory is an APK.
- static std::unique_ptr<const ApkAssets> LoadFromDir(
- const std::string& path, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
-
- // Creates a totally empty ApkAssets with no resources table and no file entries.
- static std::unique_ptr<const ApkAssets> LoadEmpty(
- package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
-
- const std::string& GetPath() const {
- return path_;
- }
+ // TODO(177101983): Remove all uses of GetPath for checking whether two ApkAssets are the same.
+ // With the introduction of ResourcesProviders, not all ApkAssets have paths. This could cause
+ // bugs when path is used for comparison because multiple ApkAssets could have the same "firendly
+ // name". Use pointer equality instead. ResourceManager caches and reuses ApkAssets so the
+ // same asset should have the same pointer.
+ const std::string& GetPath() const;
const AssetsProvider* GetAssetsProvider() const {
return assets_provider_.get();
@@ -146,53 +91,40 @@
// Returns whether the resources.arsc is allocated in RAM (not mmapped).
bool IsTableAllocated() const {
- return resources_asset_ && resources_asset_->isAllocated();
+ return resources_asset_ != nullptr && resources_asset_->isAllocated();
}
bool IsUpToDate() const;
- // Creates an Asset from a file on disk.
- static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
-
- // Creates an Asset from a file descriptor.
- //
- // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
- // must equal 0; otherwise, the asset data will be read using the `offset` into the file
- // descriptor and will be `length` bytes long.
- static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
- const char* path,
- off64_t offset = 0,
- off64_t length = kUnknownLength);
private:
- DISALLOW_COPY_AND_ASSIGN(ApkAssets);
+ static std::unique_ptr<ApkAssets> LoadImpl(std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap);
- static std::unique_ptr<const ApkAssets> LoadImpl(
- std::unique_ptr<const AssetsProvider> assets, const std::string& path,
- package_property_t property_flags,
- std::unique_ptr<const AssetsProvider> override_assets = nullptr,
- std::unique_ptr<Asset> idmap_asset = nullptr,
- std::unique_ptr<const LoadedIdmap> idmap = nullptr);
+ static std::unique_ptr<ApkAssets> LoadImpl(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap);
- static std::unique_ptr<const ApkAssets> LoadTableImpl(
- std::unique_ptr<Asset> resources_asset, const std::string& path,
- package_property_t property_flags,
- std::unique_ptr<const AssetsProvider> override_assets = nullptr);
+ ApkAssets(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<LoadedArsc> loaded_arsc,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap);
- ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
- std::string path,
- time_t last_mod_time,
- package_property_t property_flags);
-
- std::unique_ptr<const AssetsProvider> assets_provider_;
- const std::string path_;
- time_t last_mod_time_;
- package_property_t property_flags_ = 0U;
std::unique_ptr<Asset> resources_asset_;
+ std::unique_ptr<LoadedArsc> loaded_arsc_;
+
+ std::unique_ptr<AssetsProvider> assets_provider_;
+ package_property_t property_flags_ = 0U;
+
std::unique_ptr<Asset> idmap_asset_;
- std::unique_ptr<const LoadedArsc> loaded_arsc_;
- std::unique_ptr<const LoadedIdmap> loaded_idmap_;
+ std::unique_ptr<LoadedIdmap> loaded_idmap_;
};
-} // namespace android
+} // namespace android
-#endif /* APKASSETS_H_ */
+#endif // APKASSETS_H_
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 80bae20..40c91a6 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -167,8 +167,8 @@
private:
/* AssetManager needs access to our "create" functions */
friend class AssetManager;
- friend class ApkAssets;
- friend class ZipAssetsProvider;
+ friend struct ZipAssetsProvider;
+ friend struct AssetsProvider;
/*
* Create the asset from a named file on disk.
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
new file mode 100644
index 0000000..7b06947
--- /dev/null
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROIDFW_ASSETSPROVIDER_H
+#define ANDROIDFW_ASSETSPROVIDER_H
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "android-base/unique_fd.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/Idmap.h"
+#include "androidfw/LoadedArsc.h"
+#include "androidfw/misc.h"
+
+struct ZipArchive;
+
+namespace android {
+
+// Interface responsible for opening and iterating through asset files.
+struct AssetsProvider {
+ static constexpr off64_t kUnknownLength = -1;
+
+ // Opens a file for reading. If `file_exists` is not null, it will be set to `true` if the file
+ // exists. This is useful for determining if the file exists but was unable to be opened due to
+ // an I/O error.
+ std::unique_ptr<Asset> Open(const std::string& path,
+ Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
+ bool* file_exists = nullptr) const;
+
+ // Iterate over all files and directories provided by the interface. The order of iteration is
+ // stable.
+ virtual bool ForEachFile(const std::string& path,
+ const std::function<void(const StringPiece&, FileType)>& f) const = 0;
+
+ // Retrieves a name that represents the interface. This may or may not be the path of the
+ // interface source.
+ WARN_UNUSED virtual const std::string& GetDebugName() const = 0;
+
+ // Returns whether the interface provides the most recent version of its files.
+ WARN_UNUSED virtual bool IsUpToDate() const = 0;
+
+ // Creates an Asset from a file on disk.
+ static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+
+ // Creates an Asset from a file descriptor.
+ //
+ // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
+ // must equal 0; otherwise, the asset data will be read using the `offset` into the file
+ // descriptor and will be `length` bytes long.
+ static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset = 0,
+ off64_t length = AssetsProvider::kUnknownLength);
+
+ virtual ~AssetsProvider() = default;
+ protected:
+ virtual std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const = 0;
+};
+
+// Supplies assets from a zip archive.
+struct ZipAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<ZipAssetsProvider> Create(std::string path);
+ static std::unique_ptr<ZipAssetsProvider> Create(base::unique_fd fd,
+ std::string friendly_name,
+ off64_t offset = 0,
+ off64_t len = kUnknownLength);
+
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~ZipAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const override;
+
+ private:
+ struct PathOrDebugName;
+ ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, time_t last_mod_time);
+
+ struct PathOrDebugName {
+ PathOrDebugName(std::string&& value, bool is_path);
+
+ // Retrieves the path or null if this class represents a debug name.
+ WARN_UNUSED const std::string* GetPath() const;
+
+ // Retrieves a name that represents the interface. This may or may not represent a path.
+ WARN_UNUSED const std::string& GetDebugName() const;
+
+ private:
+ std::string value_;
+ bool is_path_;
+ };
+
+ std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
+ PathOrDebugName name_;
+ time_t last_mod_time_;
+};
+
+// Supplies assets from a root directory.
+struct DirectoryAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<DirectoryAssetsProvider> Create(std::string root_dir);
+
+ bool ForEachFile(const std::string& path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~DirectoryAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const override;
+
+ private:
+ explicit DirectoryAssetsProvider(std::string&& path, time_t last_mod_time);
+ std::string dir_;
+ time_t last_mod_time_;
+};
+
+// Supplies assets from a `primary` asset provider and falls back to supplying assets from the
+// `secondary` asset provider if the asset cannot be found in the `primary`.
+struct MultiAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<AssetsProvider> Create(std::unique_ptr<AssetsProvider>&& primary,
+ std::unique_ptr<AssetsProvider>&& secondary);
+
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~MultiAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode mode, bool* file_exists) const override;
+
+ private:
+ MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
+ std::unique_ptr<AssetsProvider>&& secondary);
+
+ std::unique_ptr<AssetsProvider> primary_;
+ std::unique_ptr<AssetsProvider> secondary_;
+ std::string debug_name_;
+};
+
+// Does not provide any assets.
+struct EmptyAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<AssetsProvider> Create();
+
+ bool ForEachFile(const std::string& path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~EmptyAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const override;
+};
+
+} // namespace android
+
+#endif /* ANDROIDFW_ASSETSPROVIDER_H */
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index fd9a8d13..0ded793 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -149,8 +149,8 @@
class LoadedIdmap {
public:
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
- static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data);
+ static std::unique_ptr<LoadedIdmap> Load(const StringPiece& idmap_path,
+ const StringPiece& idmap_data);
// Returns the path to the IDMAP.
std::string_view IdmapPath() const {
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 891fb90..d9225cd 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -300,17 +300,14 @@
public:
// Load a resource table from memory pointed to by `data` of size `len`.
// The lifetime of `data` must out-live the LoadedArsc returned from this method.
- // If `system` is set to true, the LoadedArsc is considered as a system provided resource.
- // If `load_as_shared_library` is set to true, the application package (0x7f) is treated
- // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an
- // ID.
- static std::unique_ptr<const LoadedArsc> Load(incfs::map_ptr<void> data,
- size_t length,
- const LoadedIdmap* loaded_idmap = nullptr,
- package_property_t property_flags = 0U);
+
+ static std::unique_ptr<LoadedArsc> Load(incfs::map_ptr<void> data,
+ size_t length,
+ const LoadedIdmap* loaded_idmap = nullptr,
+ package_property_t property_flags = 0U);
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
- static std::unique_ptr<const LoadedArsc> CreateEmpty();
+ static std::unique_ptr<LoadedArsc> CreateEmpty();
// Returns the string pool where all string resource values
// (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 3f0c7cb..b434915 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -27,6 +27,8 @@
#include "data/overlayable/R.h"
#include "data/system/R.h"
+using ::testing::NotNull;
+
namespace overlay = com::android::overlay;
namespace overlayable = com::android::overlayable;
@@ -195,7 +197,11 @@
}
TEST_F(IdmapTest, OverlayLoaderInterop) {
- auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER);
+ auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+ ASSERT_THAT(asset, NotNull());
+
+ auto loader_assets = ApkAssets::LoadTable(std::move(asset), EmptyAssetsProvider::Create(),
+ PROPERTY_LOADER);
AssetManager2 asset_manager;
asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
overlay_assets_.get()});
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 9aa3634..f356c8130 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -339,10 +339,8 @@
}
TEST(LoadedArscTest, LoadCustomLoader) {
- std::string contents;
-
- std::unique_ptr<Asset>
- asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+ auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+ ASSERT_THAT(asset, NotNull());
const StringPiece data(
reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 615bf4d..ce1d96c 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -435,6 +435,7 @@
"canvas/CanvasFrontend.cpp",
"canvas/CanvasOpBuffer.cpp",
"canvas/CanvasOpRasterizer.cpp",
+ "effects/StretchEffect.cpp",
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaRecordingCanvas.cpp",
"pipeline/skia/RenderNodeDrawable.cpp",
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 96118aa..64b8b71 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -561,7 +561,7 @@
return;
}
c->concat(invertedMatrix);
- mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop, nullptr);
+ mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop);
} else {
c->drawDrawable(drawable.get());
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index aeb60e6..609706e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -23,6 +23,7 @@
#include "Outline.h"
#include "Rect.h"
#include "RevealClip.h"
+#include "effects/StretchEffect.h"
#include "utils/MathUtils.h"
#include "utils/PaintUtils.h"
@@ -98,6 +99,10 @@
SkImageFilter* getImageFilter() const { return mImageFilter.get(); }
+ const StretchEffect& getStretchEffect() const { return mStretchEffect; }
+
+ StretchEffect& mutableStretchEffect() { return mStretchEffect; }
+
// Sets alpha, xfermode, and colorfilter from an SkPaint
// paint may be NULL, in which case defaults will be set
bool setFromPaint(const SkPaint* paint);
@@ -124,6 +129,7 @@
SkBlendMode mMode;
sk_sp<SkColorFilter> mColorFilter;
sk_sp<SkImageFilter> mImageFilter;
+ StretchEffect mStretchEffect;
};
/*
diff --git a/core/java/android/net/VpnInfo.aidl b/libs/hwui/effects/StretchEffect.cpp
similarity index 63%
copy from core/java/android/net/VpnInfo.aidl
copy to libs/hwui/effects/StretchEffect.cpp
index 8bcaa81..51cbc75 100644
--- a/core/java/android/net/VpnInfo.aidl
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,14 @@
* limitations under the License.
*/
-package android.net;
+#include "StretchEffect.h"
-parcelable VpnInfo;
+namespace android::uirenderer {
+
+sk_sp<SkImageFilter> StretchEffect::getImageFilter() const {
+ // TODO: Implement & Cache
+ // Probably need to use mutable to achieve caching
+ return nullptr;
+}
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
new file mode 100644
index 0000000..7dfd639
--- /dev/null
+++ b/libs/hwui/effects/StretchEffect.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "utils/MathUtils.h"
+
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkImageFilter.h>
+
+namespace android::uirenderer {
+
+// TODO: Inherit from base RenderEffect type?
+class StretchEffect {
+public:
+ enum class StretchInterpolator {
+ SmoothStep,
+ };
+
+ bool isEmpty() const {
+ return MathUtils::isZero(stretchDirection.x())
+ && MathUtils::isZero(stretchDirection.y());
+ }
+
+ void setEmpty() {
+ *this = StretchEffect{};
+ }
+
+ void mergeWith(const StretchEffect& other) {
+ if (other.isEmpty()) {
+ return;
+ }
+ if (isEmpty()) {
+ *this = other;
+ return;
+ }
+ stretchDirection += other.stretchDirection;
+ if (isEmpty()) {
+ return setEmpty();
+ }
+ stretchArea.join(other.stretchArea);
+ maxStretchAmount = std::max(maxStretchAmount, other.maxStretchAmount);
+ }
+
+ sk_sp<SkImageFilter> getImageFilter() const;
+
+ SkRect stretchArea {0, 0, 0, 0};
+ SkVector stretchDirection {0, 0};
+ float maxStretchAmount = 0;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 8b35d96..8023968 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -166,6 +166,31 @@
return true;
}
+static jboolean android_view_RenderNode_clearStretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ auto& stretch = renderNode->mutateStagingProperties()
+ .mutateLayerProperties().mutableStretchEffect();
+ if (stretch.isEmpty()) {
+ return false;
+ }
+ stretch.setEmpty();
+ renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
+}
+
+static jboolean android_view_RenderNode_stretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+ jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ renderNode->mutateStagingProperties().mutateLayerProperties().mutableStretchEffect().mergeWith(
+ StretchEffect{
+ .stretchArea = SkRect::MakeLTRB(left, top, right, bottom),
+ .stretchDirection = {.fX = vX, .fY = vY},
+ .maxStretchAmount = max
+ });
+ renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
+}
+
static jboolean android_view_RenderNode_hasShadow(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().hasShadow();
@@ -678,6 +703,8 @@
{ "nSetOutlinePath", "(JJF)Z", (void*) android_view_RenderNode_setOutlinePath },
{ "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty },
{ "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone },
+ { "nClearStretch", "(J)Z", (void*) android_view_RenderNode_clearStretch },
+ { "nStretch", "(JFFFFFFF)Z", (void*) android_view_RenderNode_stretch },
{ "nHasShadow", "(J)Z", (void*) android_view_RenderNode_hasShadow },
{ "nSetSpotShadowColor", "(JI)Z", (void*) android_view_RenderNode_setSpotShadowColor },
{ "nGetSpotShadowColor", "(J)I", (void*) android_view_RenderNode_getSpotShadowColor },
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index c6c9e9d..71f533c 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -194,7 +194,7 @@
canvas->concat(invertedMatrix);
const SkIRect deviceBounds = canvas->getDeviceClipBounds();
- tmpSurface->draw(canvas, deviceBounds.fLeft, deviceBounds.fTop, nullptr);
+ tmpSurface->draw(canvas, deviceBounds.fLeft, deviceBounds.fTop);
}
}
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 75815bb6..c010212 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -20,6 +20,8 @@
#include "SkiaDisplayList.h"
#include "utils/TraceUtils.h"
+#include <include/effects/SkImageFilters.h>
+
#include <optional>
namespace android {
@@ -171,11 +173,25 @@
SkPaint* paint) {
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
- properties.getImageFilter() != nullptr) {
+ properties.getImageFilter() != nullptr || !properties.getStretchEffect().isEmpty()) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
paint->setBlendMode(properties.xferMode());
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
- paint->setImageFilter(sk_ref_sp(properties.getImageFilter()));
+
+ sk_sp<SkImageFilter> imageFilter = sk_ref_sp(properties.getImageFilter());
+ sk_sp<SkImageFilter> stretchFilter = properties.getStretchEffect().getImageFilter();
+ sk_sp<SkImageFilter> filter;
+ if (imageFilter && stretchFilter) {
+ filter = SkImageFilters::Compose(
+ std::move(stretchFilter),
+ std::move(imageFilter)
+ );
+ } else if (stretchFilter) {
+ filter = std::move(stretchFilter);
+ } else {
+ filter = std::move(imageFilter);
+ }
+ paint->setImageFilter(std::move(filter));
return true;
}
return false;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 42e93b6..37a6ee7 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -492,9 +492,11 @@
if (mNativeSurface) {
// TODO(b/165985262): measure performance impact
- if (const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
- vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
- native_window_set_frame_timeline_vsync(mNativeSurface->getNativeWindow(), vsyncId);
+ const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
+ const auto inputEventId = mCurrentFrameInfo->get(FrameInfoIndex::NewestInputEvent);
+ native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), vsyncId,
+ inputEventId);
}
}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 3d188c0..3dcaffb 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -31,6 +31,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
@@ -1379,10 +1380,9 @@
* Gets the Automatic Gain Control level in dB.
*
* <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. When AGC is at a nominal level, this
- * value must be set as 0. 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.
+ * 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.
*
* <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
@@ -1775,6 +1775,7 @@
*/
@Nullable
@SystemApi
+ @SuppressLint("NullableCollection")
public Collection<CorrelationVector> getCorrelationVectors() {
return mReadOnlyCorrelationVectors;
}
@@ -1785,7 +1786,9 @@
* @hide
*/
@TestApi
- public void setCorrelationVectors(@Nullable Collection<CorrelationVector> correlationVectors) {
+ public void setCorrelationVectors(
+ @SuppressLint("NullableCollection")
+ @Nullable Collection<CorrelationVector> correlationVectors) {
if (correlationVectors == null || correlationVectors.isEmpty()) {
resetCorrelationVectors();
} else {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 96ec590..db9003e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1905,6 +1905,7 @@
@Deprecated
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
@Nullable
+ @SuppressWarnings("NullableCollection")
public List<String> getProviderPackages(@NonNull String provider) {
try {
return mService.getProviderPackages(provider);
@@ -2233,6 +2234,7 @@
* @see #getGnssCapabilities()
*/
@Nullable
+ @SuppressLint("NullableCollection")
public List<GnssAntennaInfo> getGnssAntennaInfos() {
try {
return mService.getGnssAntennaInfos();
diff --git a/location/java/android/location/provider/LocationProviderBase.java b/location/java/android/location/provider/LocationProviderBase.java
index 1306ea2..8f455cd 100644
--- a/location/java/android/location/provider/LocationProviderBase.java
+++ b/location/java/android/location/provider/LocationProviderBase.java
@@ -255,7 +255,9 @@
/**
* Implements optional custom commands.
*/
- public abstract void onSendExtraCommand(@NonNull String command, @Nullable Bundle extras);
+ public abstract void onSendExtraCommand(@NonNull String command,
+ @SuppressLint("NullableCollection")
+ @Nullable Bundle extras);
private final class Service extends ILocationProvider.Stub {
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index bcc846b..bb1dbd4 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -739,6 +739,13 @@
if (mBundle != null) {
aa.mBundle = new Bundle(mBundle);
}
+
+ // Allow the FLAG_HW_HOTWORD only for AudioSource.VOICE_RECOGNITION
+ if (mSource != MediaRecorder.AudioSource.VOICE_RECOGNITION
+ && (mFlags & FLAG_HW_HOTWORD) == FLAG_HW_HOTWORD) {
+ aa.mFlags &= ~FLAG_HW_HOTWORD;
+ }
+
return aa;
}
@@ -852,6 +859,23 @@
}
/**
+ * @hide
+ * Request for capture in hotword mode.
+ *
+ * Requests an audio path optimized for Hotword detection use cases from
+ * the low power audio DSP. This is valid only for capture with
+ * audio source {@link MediaRecorder.AudioSource#VOICE_RECOGNITION}.
+ * There is no guarantee that this mode is available on the device.
+ * @return the same Builder instance.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
+ public @NonNull Builder setHotwordMode() {
+ mFlags |= FLAG_HW_HOTWORD;
+ return this;
+ }
+
+ /**
* Specifies whether the audio may or may not be captured by other apps or the system.
*
* The default is {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL}.
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 255c15c..eedb996 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -316,6 +316,15 @@
* Not guaranteed to be supported by devices, may be emulated if not supported. */
public static final int ENCODING_PCM_32BIT = 22;
+ /** Audio data format: MPEG-H baseline profile, level 3 */
+ public static final int ENCODING_MPEGH_BL_L3 = 23;
+ /** Audio data format: MPEG-H baseline profile, level 4 */
+ public static final int ENCODING_MPEGH_BL_L4 = 24;
+ /** Audio data format: MPEG-H low complexity profile, level 3 */
+ public static final int ENCODING_MPEGH_LC_L3 = 25;
+ /** Audio data format: MPEG-H low complexity profile, level 4 */
+ public static final int ENCODING_MPEGH_LC_L4 = 26;
+
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
switch(enc) {
@@ -363,6 +372,14 @@
return "ENCODING_PCM_24BIT_PACKED";
case ENCODING_PCM_32BIT:
return "ENCODING_PCM_32BIT";
+ case ENCODING_MPEGH_BL_L3:
+ return "ENCODING_MPEGH_BL_L3";
+ case ENCODING_MPEGH_BL_L4:
+ return "ENCODING_MPEGH_BL_L4";
+ case ENCODING_MPEGH_LC_L3:
+ return "ENCODING_MPEGH_LC_L3";
+ case ENCODING_MPEGH_LC_L4:
+ return "ENCODING_MPEGH_LC_L4";
default :
return "invalid encoding " + enc;
}
@@ -594,6 +611,10 @@
case ENCODING_OPUS:
case ENCODING_PCM_24BIT_PACKED:
case ENCODING_PCM_32BIT:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return true;
default:
return false;
@@ -625,6 +646,10 @@
case ENCODING_OPUS:
case ENCODING_PCM_24BIT_PACKED:
case ENCODING_PCM_32BIT:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return true;
default:
return false;
@@ -659,6 +684,10 @@
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
case ENCODING_OPUS:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return false;
case ENCODING_INVALID:
default:
@@ -693,6 +722,10 @@
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
case ENCODING_OPUS:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return false;
case ENCODING_INVALID:
default:
@@ -975,6 +1008,10 @@
case ENCODING_OPUS:
case ENCODING_PCM_24BIT_PACKED:
case ENCODING_PCM_32BIT:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1197,7 +1234,11 @@
ENCODING_DOLBY_MAT,
ENCODING_OPUS,
ENCODING_PCM_24BIT_PACKED,
- ENCODING_PCM_32BIT }
+ ENCODING_PCM_32BIT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
@@ -1213,6 +1254,10 @@
ENCODING_AC4,
ENCODING_E_AC3_JOC,
ENCODING_DOLBY_MAT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4,
};
/** @hide */
@@ -1225,7 +1270,11 @@
ENCODING_DOLBY_TRUEHD,
ENCODING_AC4,
ENCODING_E_AC3_JOC,
- ENCODING_DOLBY_MAT }
+ ENCODING_DOLBY_MAT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface SurroundSoundEncoding {}
@@ -1259,6 +1308,14 @@
return "Dolby Atmos in Dolby Digital Plus";
case ENCODING_DOLBY_MAT:
return "Dolby MAT";
+ case ENCODING_MPEGH_BL_L3:
+ return "MPEG-H 3D Audio baseline profile level 3";
+ case ENCODING_MPEGH_BL_L4:
+ return "MPEG-H 3D Audio baseline profile level 4";
+ case ENCODING_MPEGH_LC_L3:
+ return "MPEG-H 3D Audio low complexity profile level 3";
+ case ENCODING_MPEGH_LC_L4:
+ return "MPEG-H 3D Audio low complexity profile level 4";
default:
return "Unknown surround sound format";
}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index c2ce7d3..b196437 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
@@ -67,7 +68,14 @@
* fill with the new audio data. The size of this buffer, specified during the construction,
* determines how long an AudioRecord can record before "over-running" data that has not
* been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
- * the total recording buffer size.
+ * the total recording buffer size.</p>
+ * <p>
+ * Applications creating an AudioRecord instance need
+ * {@link android.Manifest.permission#RECORD_AUDIO} or the Builder will throw
+ * {@link java.lang.UnsupportedOperationException} on
+ * {@link android.media.AudioRecord.Builder#build build()},
+ * and the constructor will return an instance in state
+ * {@link #STATE_UNINITIALIZED}.</p>
*/
public class AudioRecord implements AudioRouting, MicrophoneDirection,
AudioRecordingMonitor, AudioRecordingMonitorClient
@@ -297,6 +305,7 @@
* smaller than getMinBufferSize() will result in an initialization failure.
* @throws java.lang.IllegalArgumentException
*/
+ @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes)
throws IllegalArgumentException {
@@ -334,6 +343,7 @@
* @throws IllegalArgumentException
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int sessionId) throws IllegalArgumentException {
mRecordingState = RECORDSTATE_STOPPED;
@@ -718,6 +728,7 @@
* were incompatible, or if they are not supported by the device,
* or if the device was not available.
*/
+ @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
public AudioRecord build() throws UnsupportedOperationException {
if (mAudioPlaybackCaptureConfiguration != null) {
return buildAudioPlaybackCaptureRecord();
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9657b25e..4d7ed11 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1089,7 +1089,7 @@
private int[] mSampleRates;
private Range<Integer>[] mSampleRateRanges;
- private int mMaxInputChannelCount;
+ private Range<Integer>[] mInputChannelRanges;
private static final int MAX_INPUT_CHANNEL_COUNT = 30;
@@ -1119,11 +1119,61 @@
}
/**
- * Returns the maximum number of input channels supported. The codec
- * supports any number of channels between 1 and this maximum value.
+ * Returns the maximum number of input channels supported.
+ *
+ * Through {@link android.os.Build.VERSION_CODES#R}, this method indicated support
+ * for any number of input channels between 1 and this maximum value.
+ *
+ * As of {@link android.os.Build.VERSION_CODES#S},
+ * the implied lower limit of 1 channel is no longer valid.
+ * As of {@link android.os.Build.VERSION_CODES#S}, {@link #getMaxInputChannelCount} is
+ * superseded by {@link #getInputChannelCountRanges},
+ * which returns an array of ranges of channels.
+ * The {@link #getMaxInputChannelCount} method will return the highest value
+ * in the ranges returned by {@link #getInputChannelCountRanges}
+ *
*/
public int getMaxInputChannelCount() {
- return mMaxInputChannelCount;
+ int overall_max = 0;
+ for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
+ int lmax = mInputChannelRanges[i].getUpper();
+ if (lmax > overall_max) {
+ overall_max = lmax;
+ }
+ }
+ return overall_max;
+ }
+
+ /**
+ * Returns the minimum number of input channels supported.
+ * This is often 1, but does vary for certain mime types.
+ *
+ * This returns the lowest channel count in the ranges returned by
+ * {@link #getInputChannelCountRanges}.
+ */
+ public int getMinInputChannelCount() {
+ int overall_min = MAX_INPUT_CHANNEL_COUNT;
+ for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
+ int lmin = mInputChannelRanges[i].getLower();
+ if (lmin < overall_min) {
+ overall_min = lmin;
+ }
+ }
+ return overall_min;
+ }
+
+ /*
+ * Returns an array of ranges representing the number of input channels supported.
+ * The codec supports any number of input channels within this range.
+ *
+ * This supersedes the {@link #getMaxInputChannelCount} method.
+ *
+ * For many codecs, this will be a single range [1..N], for some N.
+ */
+ @SuppressLint("ArrayReturn")
+ @NonNull
+ public Range<Integer>[] getInputChannelCountRanges() {
+ return Arrays.copyOf(mInputChannelRanges, mInputChannelRanges.length);
}
/* no public constructor */
@@ -1146,7 +1196,7 @@
private void initWithPlatformLimits() {
mBitrateRange = Range.create(0, Integer.MAX_VALUE);
- mMaxInputChannelCount = MAX_INPUT_CHANNEL_COUNT;
+ mInputChannelRanges = new Range[] {Range.create(1, MAX_INPUT_CHANNEL_COUNT)};
// mBitrateRange = Range.create(1, 320000);
final int minSampleRate = SystemProperties.
getInt("ro.mediacodec.min_sample_rate", 7350);
@@ -1158,9 +1208,12 @@
private boolean supports(Integer sampleRate, Integer inputChannels) {
// channels and sample rates are checked orthogonally
- if (inputChannels != null &&
- (inputChannels < 1 || inputChannels > mMaxInputChannelCount)) {
- return false;
+ if (inputChannels != null) {
+ int ix = Utils.binarySearchDistinctRanges(
+ mInputChannelRanges, inputChannels);
+ if (ix < 0) {
+ return false;
+ }
}
if (sampleRate != null) {
int ix = Utils.binarySearchDistinctRanges(
@@ -1292,12 +1345,28 @@
} else if (sampleRateRange != null) {
limitSampleRates(new Range[] { sampleRateRange });
}
- applyLimits(maxChannels, bitRates);
+
+ Range<Integer> channelRange = Range.create(1, maxChannels);
+
+ applyLimits(new Range[] { channelRange }, bitRates);
}
- private void applyLimits(int maxInputChannels, Range<Integer> bitRates) {
- mMaxInputChannelCount = Range.create(1, mMaxInputChannelCount)
- .clamp(maxInputChannels);
+ private void applyLimits(Range<Integer>[] inputChannels, Range<Integer> bitRates) {
+
+ // clamp & make a local copy
+ Range<Integer>[] myInputChannels = new Range[inputChannels.length];
+ for (int i = 0; i < inputChannels.length; i++) {
+ int lower = inputChannels[i].clamp(1);
+ int upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
+ myInputChannels[i] = Range.create(lower, upper);
+ }
+
+ // sort, intersect with existing, & save channel list
+ sortDistinctRanges(myInputChannels);
+ Range<Integer>[] joinedChannelList =
+ intersectSortedDistinctRanges(myInputChannels, mInputChannelRanges);
+ mInputChannelRanges = joinedChannelList;
+
if (bitRates != null) {
mBitrateRange = mBitrateRange.intersect(bitRates);
}
@@ -1305,6 +1374,7 @@
private void parseFromInfo(MediaFormat info) {
int maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
+ Range<Integer>[] channels = new Range[] { Range.create(1, maxInputChannels)};
Range<Integer> bitRates = POSITIVE_INTEGERS;
if (info.containsKey("sample-rate-ranges")) {
@@ -1315,17 +1385,38 @@
}
limitSampleRates(rateRanges);
}
- if (info.containsKey("max-channel-count")) {
+
+ // we will prefer channel-ranges over max-channel-count
+ if (info.containsKey("channel-ranges")) {
+ String[] channelStrings = info.getString("channel-ranges").split(",");
+ Range<Integer>[] channelRanges = new Range[channelStrings.length];
+ for (int i = 0; i < channelStrings.length; i++) {
+ channelRanges[i] = Utils.parseIntRange(channelStrings[i], null);
+ }
+ channels = channelRanges;
+ } else if (info.containsKey("channel-range")) {
+ Range<Integer> oneRange = Utils.parseIntRange(info.getString("channel-range"),
+ null);
+ channels = new Range[] { oneRange };
+ } else if (info.containsKey("max-channel-count")) {
maxInputChannels = Utils.parseIntSafely(
info.getString("max-channel-count"), maxInputChannels);
+ if (maxInputChannels == 0) {
+ channels = new Range[] {Range.create(0, 0)};
+ } else {
+ channels = new Range[] {Range.create(1, maxInputChannels)};
+ }
} else if ((mParent.mError & ERROR_UNSUPPORTED) != 0) {
maxInputChannels = 0;
+ channels = new Range[] {Range.create(0, 0)};
}
+
if (info.containsKey("bitrate-range")) {
bitRates = bitRates.intersect(
Utils.parseIntRange(info.getString("bitrate-range"), bitRates));
}
- applyLimits(maxInputChannels, bitRates);
+
+ applyLimits(channels, bitRates);
}
/** @hide */
@@ -1334,7 +1425,7 @@
if (mBitrateRange.getLower().equals(mBitrateRange.getUpper())) {
format.setInteger(MediaFormat.KEY_BIT_RATE, mBitrateRange.getLower());
}
- if (mMaxInputChannelCount == 1) {
+ if (getMaxInputChannelCount() == 1) {
// mono-only format
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 68237de..5e732f9 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -879,38 +879,37 @@
/**
* Interface for receiving events about media routing changes.
*/
- public static class Callback {
-
+ public interface Callback {
/**
* Called when routes are added.
* @param routes the list of routes that have been added. It's never empty.
*/
- public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {}
+ default void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {}
/**
* Called when routes are removed.
* @param routes the list of routes that have been removed. It's never empty.
*/
- public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {}
+ default void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {}
/**
* Called when routes are changed.
* @param routes the list of routes that have been changed. It's never empty.
*/
- public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
+ default void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
/**
* Called when a session is changed.
* @param session the updated session
*/
- public void onSessionUpdated(@NonNull RoutingSessionInfo session) {}
+ default void onSessionUpdated(@NonNull RoutingSessionInfo session) {}
/**
* Called when a session is released.
* @param session the released session.
* @see #releaseSession(RoutingSessionInfo)
*/
- public void onSessionReleased(@NonNull RoutingSessionInfo session) {}
+ default void onSessionReleased(@NonNull RoutingSessionInfo session) {}
/**
* Called when media is transferred.
@@ -918,13 +917,13 @@
* @param oldSession the previous session
* @param newSession the new session or {@code null} if the session is released.
*/
- public void onTransferred(@NonNull RoutingSessionInfo oldSession,
+ default void onTransferred(@NonNull RoutingSessionInfo oldSession,
@Nullable RoutingSessionInfo newSession) { }
/**
* Called when {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} fails.
*/
- public void onTransferFailed(@NonNull RoutingSessionInfo session,
+ default void onTransferFailed(@NonNull RoutingSessionInfo session,
@NonNull MediaRoute2Info route) { }
/**
@@ -933,7 +932,7 @@
* @param packageName the package name of the application
* @param preferredFeatures the list of preferred route features set by an application.
*/
- public void onPreferredFeaturesChanged(@NonNull String packageName,
+ default void onPreferredFeaturesChanged(@NonNull String packageName,
@NonNull List<String> preferredFeatures) {}
/**
@@ -946,7 +945,7 @@
* {@link MediaRoute2ProviderService#REASON_ROUTE_NOT_AVAILABLE},
* {@link MediaRoute2ProviderService#REASON_INVALID_COMMAND},
*/
- public void onRequestFailed(int reason) {}
+ default void onRequestFailed(int reason) {}
}
final class CallbackRecord {
diff --git a/media/java/android/media/MediaServiceManager.java b/media/java/android/media/MediaServiceManager.java
index 96bff4f..b899559 100644
--- a/media/java/android/media/MediaServiceManager.java
+++ b/media/java/android/media/MediaServiceManager.java
@@ -33,6 +33,7 @@
public class MediaServiceManager {
private static final String MEDIA_SESSION_SERVICE = "media_session";
private static final String MEDIA_TRANSCODING_SERVICE = "media.transcoding";
+ private static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
/**
* @hide
@@ -79,4 +80,12 @@
public ServiceRegisterer getMediaTranscodingServiceRegisterer() {
return new ServiceRegisterer(MEDIA_TRANSCODING_SERVICE);
}
+
+ /**
+ * Returns {@link ServiceRegisterer} for MEDIA_COMMUNICATION_SERVICE.
+ */
+ @NonNull
+ public ServiceRegisterer getMediaCommunicationServiceRegisterer() {
+ return new ServiceRegisterer(MEDIA_COMMUNICATION_SERVICE);
+ }
}
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionManager.java b/media/java/android/media/musicrecognition/MusicRecognitionManager.java
index 6bbcfd3..b183eaf 100644
--- a/media/java/android/media/musicrecognition/MusicRecognitionManager.java
+++ b/media/java/android/media/musicrecognition/MusicRecognitionManager.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
@@ -90,7 +91,9 @@
* supplied bundle
*/
void onRecognitionSucceeded(@NonNull RecognitionRequest recognitionRequest,
- @NonNull MediaMetadata result, @Nullable Bundle extras);
+ @NonNull MediaMetadata result,
+ @SuppressLint("NullableCollection")
+ @Nullable Bundle extras);
/**
* Invoked when the search is not successful (possibly but not necessarily due to error).
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java
index e2071b8..04b4c39b 100644
--- a/media/java/android/media/musicrecognition/MusicRecognitionService.java
+++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
@@ -53,7 +54,9 @@
* @param extras extra data to be supplied back to the caller. Note that all executable
* parameters and file descriptors would be removed from the supplied bundle
*/
- void onRecognitionSucceeded(@NonNull MediaMetadata result, @Nullable Bundle extras);
+ void onRecognitionSucceeded(@NonNull MediaMetadata result,
+ @SuppressLint("NullableCollection")
+ @Nullable Bundle extras);
/**
* Call this method if the search does not find a result on an error occurred.
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 4d835b2..8525e99 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
@@ -1017,6 +1018,7 @@
* failed.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public List<FrontendInfo> getAvailableFrontendInfos() {
FrontendInfo[] feInfoList = getFrontendInfoListInternal();
if (feInfoList == null) {
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 9f327c9..51b685a 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -284,6 +284,12 @@
/**
* Configures the filter.
*
+ * <p>Recofiguring must happen after stopping the filter.
+ *
+ * <p>When stopping, reconfiguring and restarting the filter, the client should discard all
+ * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
+ * using the events from the previous configuration.
+ *
* @param config the configuration of the filter.
* @return result status of the operation.
*/
@@ -393,6 +399,10 @@
*
* <p>Does nothing if the filter is already started.
*
+ * <p>When stopping, reconfiguring and restarting the filter, the client should discard all
+ * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
+ * using the events from the previous configuration.
+ *
* @return result status of the operation.
*/
@Result
@@ -409,6 +419,12 @@
*
* <p>Does nothing if the filter is stopped or not started.
*
+ * <p>Filter must be stopped to reconfigure.
+ *
+ * <p>When stopping, reconfiguring and restarting the filter, the client should discard all
+ * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
+ * using the events from the previous configuration.
+ *
* @return result status of the operation.
*/
@Result
diff --git a/media/java/android/media/tv/tuner/filter/RestartEvent.java b/media/java/android/media/tv/tuner/filter/RestartEvent.java
index 0696301..9c5992a 100644
--- a/media/java/android/media/tv/tuner/filter/RestartEvent.java
+++ b/media/java/android/media/tv/tuner/filter/RestartEvent.java
@@ -19,17 +19,29 @@
import android.annotation.SystemApi;
/**
- * An Event that the client would reveice after stopping, reconfiguring and restarting a filter.
+ * An Event that the client would receive after starting a filter. This event is optional to be
+ * received on the newly opened and started filter. It must be received after stopping,
+ * reconfiguring and restarting a Filter to differentiate the valid reconfigured events from the
+ * previous events.
*
* <p>After stopping and restarting the filter, the client has to discard all coming events until
* it receives {@link RestartEvent} to avoid using the events from the previous configuration.
*
* <p>Recofiguring must happen after stopping the filter.
*
+ * @see Filter#stop()
+ * @see Filter#start()
+ * @see Filter#configure(FilterConfiguration)
+ *
* @hide
*/
@SystemApi
public final class RestartEvent extends FilterEvent {
+ /**
+ * The stard id reserved for the newly opened filter's first start event.
+ */
+ public static final int NEW_FILTER_FIRST_START_ID = 0;
+
private final int mStartId;
// This constructor is used by JNI code only
@@ -38,13 +50,13 @@
}
/**
- * Gets the start id.
+ * Gets the start id sent via the current Restart Event.
*
- * <p>An unique ID to mark the start point of receiving the valid filter events after
- * reconfiguring. It must be sent at least once in the first event after the filter is
- * restarted.
+ * <p>An unique ID to mark the start point of receiving the valid reconfigured filter events.
+ * The client must receive at least once after the filter is reconfigured and restarted.
*
- * <p>0 is reserved for the newly opened filter's first start. It's optional to be received.
+ * <p>{@link #NEW_FILTER_FIRST_START_ID} is reserved for the newly opened filter's first start.
+ * It's optional to be received.
*/
public int getStartId() {
return mStartId;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index decf68f..67a2c49 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -166,6 +166,11 @@
"tv_tuner_aidl_interface-ndk_platform",
"tv_tuner_resource_manager_aidl_interface-ndk_platform"
],
+
+ static_libs: [
+ "libaidlcommonsupport",
+ ],
+
defaults: [
"libcodec2-impl-defaults",
],
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp
index c064de2..08a8d89 100644
--- a/media/jni/android_media_MediaMetricsJNI.cpp
+++ b/media/jni/android_media_MediaMetricsJNI.cpp
@@ -18,7 +18,6 @@
#include <binder/Parcel.h>
#include <jni.h>
-#include <media/IMediaMetricsService.h>
#include <media/MediaMetricsItem.h>
#include <nativehelper/JNIHelp.h>
#include <variant>
@@ -151,12 +150,7 @@
return (jint)BAD_VALUE;
}
- sp<IMediaMetricsService> service = mediametrics::BaseItem::getService();
- if (service == nullptr) {
- ALOGW("Cannot retrieve mediametrics service");
- return (jint)NO_INIT;
- }
- return (jint)service->submitBuffer((char *)buffer, length);
+ return (jint)mediametrics::BaseItem::submitBuffer((char *)buffer, length);
}
// Helper function to convert a native PersistableBundle to a Java
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index eb63d76..9ec84d9 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1458,7 +1458,7 @@
ALOGE("frontend is not initialized");
return (int)Result::INVALID_STATE;
}
- return (int) mFeClient->tune(settings,settingsExt1_1);
+ return (int) mFeClient->tune(settings, settingsExt1_1);
}
int JTuner::stopTune() {
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index e290c60..1a2f8c0 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -142,7 +142,16 @@
}
sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) {
- // TODO: pending aidl interface
+ if (mTunerDemux != NULL) {
+ shared_ptr<ITunerDvr> tunerDvr;
+ shared_ptr<TunerDvrCallback> callback =
+ ::ndk::SharedRefBase::make<TunerDvrCallback>(cb);
+ Status s = mTunerDemux->openDvr((int)dvbType, bufferSize, callback, &tunerDvr);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ return new DvrClient(tunerDvr);
+ }
if (mDemux != NULL) {
sp<HidlDvrCallback> callback = new HidlDvrCallback(cb);
@@ -178,7 +187,10 @@
}
Result DemuxClient::close() {
- // TODO: pending aidl interface
+ if (mTunerDemux != NULL) {
+ Status s = mTunerDemux->close();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDemux != NULL) {
Result res = mDemux->close();
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index be592af..0400485 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -210,14 +210,13 @@
return res;
}
- AidlMQDesc* aidlMqDesc = NULL;
- s = mTunerDvr->getQueueDesc(aidlMqDesc);
+ AidlMQDesc aidlMqDesc;
+ s = mTunerDvr->getQueueDesc(&aidlMqDesc);
res = ClientHelper::getServiceSpecificErrorCode(s);
if (res != Result::SUCCESS) {
return res;
}
-
- mDvrMQ = new (nothrow) AidlMQ(*aidlMqDesc);
+ mDvrMQ = new (nothrow) AidlMQ(aidlMqDesc);
EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag);
return res;
}
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index bcef0a2..6b78817 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -16,15 +16,33 @@
#define LOG_TAG "FilterClient"
+#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <utils/Log.h>
#include "FilterClient.h"
-using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
+using ::aidl::android::media::tv::tuner::TunerDemuxIpAddressSettings;
+using ::aidl::android::media::tv::tuner::TunerFilterAlpConfiguration;
+using ::aidl::android::media::tv::tuner::TunerFilterIpConfiguration;
+using ::aidl::android::media::tv::tuner::TunerFilterMmtpConfiguration;
+using ::aidl::android::media::tv::tuner::TunerFilterMonitorEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterScIndexMask;
+using ::aidl::android::media::tv::tuner::TunerFilterSectionBits;
+using ::aidl::android::media::tv::tuner::TunerFilterSectionCondition;
+using ::aidl::android::media::tv::tuner::TunerFilterSectionTableInfo;
+using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
+using ::aidl::android::media::tv::tuner::TunerFilterTlvConfiguration;
+using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration;
+
using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
+using ::android::hardware::tv::tuner::V1_0::DemuxStreamId;
+using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_1::ScramblingStatus;
namespace android {
@@ -48,7 +66,6 @@
void FilterClient::setHidlFilter(sp<IFilter> filter) {
mFilter = filter;
mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter);
- handleAvShareMemory();
}
int FilterClient::read(uint8_t* buffer, int size) {
@@ -66,23 +83,20 @@
}
SharedHandleInfo FilterClient::getAvSharedHandleInfo() {
+ handleAvShareMemory();
SharedHandleInfo info{
- .sharedHandle = NULL,
- .size = 0,
+ .sharedHandle = mAvSharedHandle,
+ .size = mAvSharedMemSize,
};
- // TODO: pending aidl interface
-
- if (mFilter_1_1 != NULL) {
- info.sharedHandle = mAvSharedHandle;
- info.size = mAvSharedMemSize;
- }
-
return info;
}
Result FilterClient::configure(DemuxFilterSettings configure) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->configure(getAidlFilterSettings(configure));
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter != NULL) {
return mFilter->configure(configure);
@@ -122,7 +136,10 @@
}
Result FilterClient::start() {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->start();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter != NULL) {
return mFilter->start();
@@ -132,7 +149,10 @@
}
Result FilterClient::stop() {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->stop();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter != NULL) {
return mFilter->stop();
@@ -142,7 +162,10 @@
}
Result FilterClient::flush() {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->flush();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter != NULL) {
return mFilter->flush();
@@ -192,7 +215,10 @@
}
Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->releaseAvHandle(makeToAidl(handle), avDataId);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter != NULL) {
return mFilter->releaseAvHandle(hidl_handle(handle), avDataId);
@@ -216,13 +242,18 @@
}
Result FilterClient::close() {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->close();
+ closeAvSharedMemory();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter != NULL) {
Result res = mFilter->close();
if (res == Result::SUCCESS) {
mFilter = NULL;
}
+ closeAvSharedMemory();
return res;
}
@@ -269,13 +300,596 @@
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
-Status TunerFilterCallback::onFilterEvent(vector<TunerFilterEvent>* /*filterEvent*/) {
- // TODO: complete onFilterEvent
+Status TunerFilterCallback::onFilterEvent(const vector<TunerFilterEvent>& filterEvents) {
+ if (mFilterClientCallback == NULL) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
+ }
+
+ if (filterEvents.size() == 0) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_ARGUMENT));
+ }
+
+ DemuxFilterEvent event;
+ DemuxFilterEventExt eventExt;
+ getHidlFilterEvent(filterEvents, event, eventExt);
+ if (eventExt.events.size() > 0) {
+ mFilterClientCallback->onFilterEvent_1_1(event, eventExt);
+ } else {
+ mFilterClientCallback->onFilterEvent(event);
+ }
+
return Status::ok();
}
/////////////// FilterClient Helper Methods ///////////////////////
+TunerFilterConfiguration FilterClient::getAidlFilterSettings(DemuxFilterSettings configure) {
+ TunerFilterConfiguration config;
+ switch (configure.getDiscriminator()) {
+ case DemuxFilterSettings::hidl_discriminator::ts:
+ return getAidlTsSettings(configure.ts());
+ case DemuxFilterSettings::hidl_discriminator::mmtp:
+ return getAidlMmtpSettings(configure.mmtp());
+ case DemuxFilterSettings::hidl_discriminator::ip:
+ return getAidlIpSettings(configure.ip());
+ case DemuxFilterSettings::hidl_discriminator::tlv:
+ return getAidlTlvSettings(configure.tlv());
+ case DemuxFilterSettings::hidl_discriminator::alp:
+ return getAidlAlpSettings(configure.alp());
+ default:
+ break;
+ }
+ ALOGE("Wrong DemuxFilterSettings union.");
+ return config;
+}
+
+TunerFilterConfiguration FilterClient::getAidlTsSettings(DemuxTsFilterSettings ts) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (ts.filterSettings.getDiscriminator()) {
+ case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::av: {
+ filterSettings.set<TunerFilterSettings::av>(
+ getAidlAvSettings(ts.filterSettings.av()));
+ break;
+ }
+ case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(ts.filterSettings.section()));
+ break;
+ }
+ case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::pesData: {
+ filterSettings.set<TunerFilterSettings::pesData>(
+ getAidlPesDataSettings(ts.filterSettings.pesData()));
+ break;
+ }
+ case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::record: {
+ filterSettings.set<TunerFilterSettings::record>(
+ getAidlRecordSettings(ts.filterSettings.record()));
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerFilterTsConfiguration aidlTs{
+ .tpid = static_cast<char16_t>(ts.tpid),
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::ts>(aidlTs);
+
+ return config;
+}
+
+TunerFilterConfiguration FilterClient::getAidlMmtpSettings(DemuxMmtpFilterSettings mmtp) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (mmtp.filterSettings.getDiscriminator()) {
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::av: {
+ filterSettings.set<TunerFilterSettings::av>(
+ getAidlAvSettings(mmtp.filterSettings.av()));
+ break;
+ }
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(mmtp.filterSettings.section()));
+ break;
+ }
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::pesData: {
+ filterSettings.set<TunerFilterSettings::pesData>(
+ getAidlPesDataSettings(mmtp.filterSettings.pesData()));
+ break;
+ }
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::record: {
+ filterSettings.set<TunerFilterSettings::record>(
+ getAidlRecordSettings(mmtp.filterSettings.record()));
+ break;
+ }
+ case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::download: {
+ filterSettings.set<TunerFilterSettings::download>(
+ getAidlDownloadSettings(mmtp.filterSettings.download()));
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerFilterMmtpConfiguration aidlMmtp{
+ .mmtpPid = static_cast<char16_t>(mmtp.mmtpPid),
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::mmtp>(aidlMmtp);
+
+ return config;
+}
+
+TunerFilterConfiguration FilterClient::getAidlIpSettings(DemuxIpFilterSettings ip) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (ip.filterSettings.getDiscriminator()) {
+ case DemuxIpFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(ip.filterSettings.section()));
+ break;
+ }
+ case DemuxIpFilterSettings::FilterSettings::hidl_discriminator::bPassthrough: {
+ filterSettings.set<TunerFilterSettings::isPassthrough>(
+ ip.filterSettings.bPassthrough());
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerDemuxIpAddressSettings ipAddr{
+ .srcPort = static_cast<char16_t>(ip.ipAddr.srcPort),
+ .dstPort = static_cast<char16_t>(ip.ipAddr.dstPort),
+ };
+ getAidlIpAddress(ip.ipAddr, ipAddr.srcIpAddress, ipAddr.dstIpAddress);
+
+ TunerFilterIpConfiguration aidlIp{
+ .ipAddr = ipAddr,
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::ip>(aidlIp);
+
+ return config;
+}
+
+void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr,
+ TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress) {
+ switch (ipAddr.srcIpAddress.getDiscriminator()) {
+ case DemuxIpAddress::SrcIpAddress::hidl_discriminator::v4: {
+ int size = ipAddr.srcIpAddress.v4().size();
+ srcIpAddress.isIpV6 = false;
+ srcIpAddress.addr.resize(ipAddr.srcIpAddress.v4().size());
+ copy(&ipAddr.srcIpAddress.v4()[0], &ipAddr.srcIpAddress.v4()[size],
+ srcIpAddress.addr.begin());
+ break;
+ }
+ case DemuxIpAddress::SrcIpAddress::hidl_discriminator::v6: {
+ int size = ipAddr.srcIpAddress.v6().size();
+ srcIpAddress.isIpV6 = true;
+ srcIpAddress.addr.resize(size);
+ copy(&ipAddr.srcIpAddress.v6()[0], &ipAddr.srcIpAddress.v6()[size],
+ srcIpAddress.addr.begin());
+ break;
+ }
+ default:
+ break;
+ }
+ switch (ipAddr.dstIpAddress.getDiscriminator()) {
+ case DemuxIpAddress::DstIpAddress::hidl_discriminator::v4: {
+ int size = ipAddr.dstIpAddress.v4().size();
+ dstIpAddress.isIpV6 = false;
+ dstIpAddress.addr.resize(size);
+ copy(&ipAddr.dstIpAddress.v4()[0], &ipAddr.dstIpAddress.v4()[size],
+ dstIpAddress.addr.begin());
+ break;
+ }
+ case DemuxIpAddress::DstIpAddress::hidl_discriminator::v6: {
+ int size = ipAddr.dstIpAddress.v6().size();
+ dstIpAddress.isIpV6 = true;
+ dstIpAddress.addr.resize(size);
+ copy(&ipAddr.dstIpAddress.v6()[0], &ipAddr.dstIpAddress.v6()[size],
+ dstIpAddress.addr.begin());
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+TunerFilterConfiguration FilterClient::getAidlTlvSettings(DemuxTlvFilterSettings tlv) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (tlv.filterSettings.getDiscriminator()) {
+ case DemuxTlvFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(tlv.filterSettings.section()));
+ break;
+ }
+ case DemuxTlvFilterSettings::FilterSettings::hidl_discriminator::bPassthrough: {
+ filterSettings.set<TunerFilterSettings::isPassthrough>(
+ tlv.filterSettings.bPassthrough());
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerFilterTlvConfiguration aidlTlv{
+ .packetType = static_cast<int8_t>(tlv.packetType),
+ .isCompressedIpPacket = tlv.isCompressedIpPacket,
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::tlv>(aidlTlv);
+
+ return config;
+}
+
+TunerFilterConfiguration FilterClient::getAidlAlpSettings(DemuxAlpFilterSettings alp) {
+ TunerFilterConfiguration config;
+ TunerFilterSettings filterSettings;
+ switch (alp.filterSettings.getDiscriminator()) {
+ case DemuxAlpFilterSettings::FilterSettings::hidl_discriminator::section: {
+ filterSettings.set<TunerFilterSettings::section>(
+ getAidlSectionSettings(alp.filterSettings.section()));
+ break;
+ }
+ default:
+ filterSettings.set<TunerFilterSettings::nothing>(true);
+ break;
+ }
+
+ TunerFilterAlpConfiguration aidlAlp{
+ .packetType = static_cast<int8_t>(alp.packetType),
+ .lengthType = static_cast<int8_t>(alp.lengthType),
+ .filterSettings = filterSettings,
+ };
+ config.set<TunerFilterConfiguration::alp>(aidlAlp);
+
+ return config;
+}
+
+TunerFilterAvSettings FilterClient::getAidlAvSettings(DemuxFilterAvSettings hidlAv) {
+ TunerFilterAvSettings aidlAv{
+ .isPassthrough = hidlAv.isPassthrough,
+ };
+ return aidlAv;
+}
+
+TunerFilterSectionSettings FilterClient::getAidlSectionSettings(
+ DemuxFilterSectionSettings hidlSection) {
+ TunerFilterSectionSettings aidlSection;
+
+ switch (hidlSection.condition.getDiscriminator()) {
+ case DemuxFilterSectionSettings::Condition::hidl_discriminator::sectionBits: {
+ TunerFilterSectionBits sectionBits;
+ auto hidlSectionBits = hidlSection.condition.sectionBits();
+ sectionBits.filter.resize(hidlSectionBits.filter.size());
+ sectionBits.mask.resize(hidlSectionBits.mask.size());
+ sectionBits.mode.resize(hidlSectionBits.mode.size());
+ copy(hidlSectionBits.filter.begin(), hidlSectionBits.filter.end(),
+ hidlSectionBits.filter.begin());
+ copy(hidlSectionBits.mask.begin(), hidlSectionBits.mask.end(),
+ hidlSectionBits.mask.begin());
+ copy(hidlSectionBits.mode.begin(), hidlSectionBits.mode.end(),
+ hidlSectionBits.mode.begin());
+ aidlSection.condition.set<TunerFilterSectionCondition::sectionBits>(sectionBits);
+ break;
+ }
+ case DemuxFilterSectionSettings::Condition::hidl_discriminator::tableInfo: {
+ TunerFilterSectionTableInfo tableInfo{
+ .tableId = static_cast<char16_t>(hidlSection.condition.tableInfo().tableId),
+ .version = static_cast<char16_t>(hidlSection.condition.tableInfo().version),
+ };
+ aidlSection.condition.set<TunerFilterSectionCondition::tableInfo>(tableInfo);
+ break;
+ }
+ }
+ aidlSection.isCheckCrc = hidlSection.isCheckCrc;
+ aidlSection.isRepeat = hidlSection.isRepeat;
+ aidlSection.isRaw = hidlSection.isRaw;
+ return aidlSection;
+}
+
+TunerFilterPesDataSettings FilterClient::getAidlPesDataSettings(
+ DemuxFilterPesDataSettings hidlPesData) {
+ TunerFilterPesDataSettings aidlPesData{
+ .streamId = static_cast<char16_t>(hidlPesData.streamId),
+ .isRaw = hidlPesData.isRaw,
+ };
+ return aidlPesData;
+}
+
+TunerFilterRecordSettings FilterClient::getAidlRecordSettings(
+ DemuxFilterRecordSettings hidlRecord) {
+ TunerFilterScIndexMask mask;
+ switch (hidlRecord.scIndexMask.getDiscriminator()) {
+ case DemuxFilterRecordSettings::ScIndexMask::hidl_discriminator::sc: {
+ mask.set<TunerFilterScIndexMask::sc>(hidlRecord.scIndexMask.sc());
+ break;
+ }
+ case DemuxFilterRecordSettings::ScIndexMask::hidl_discriminator::scHevc: {
+ mask.set<TunerFilterScIndexMask::scHevc>(hidlRecord.scIndexMask.scHevc());
+ break;
+ }
+ default:
+ break;
+ }
+ TunerFilterRecordSettings aidlRecord{
+ .tsIndexMask = static_cast<int32_t>(hidlRecord.tsIndexMask),
+ .scIndexType = static_cast<int32_t>(hidlRecord.scIndexType),
+ .scIndexMask = mask,
+ };
+ return aidlRecord;
+}
+
+TunerFilterDownloadSettings FilterClient::getAidlDownloadSettings(
+ DemuxFilterDownloadSettings hidlDownload) {
+ TunerFilterDownloadSettings aidlDownload{
+ .downloadId = static_cast<int32_t>(hidlDownload.downloadId),
+ };
+ return aidlDownload;
+}
+
+void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ switch (filterEvents[0].getTag()) {
+ case TunerFilterEvent::media: {
+ getHidlMediaEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::section: {
+ getHidlSectionEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::pes: {
+ getHidlPesEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::tsRecord: {
+ getHidlTsRecordEvent(filterEvents, event, eventExt);
+ break;
+ }
+ case TunerFilterEvent::mmtpRecord: {
+ getHidlMmtpRecordEvent(filterEvents, event, eventExt);
+ break;
+ }
+ case TunerFilterEvent::download: {
+ getHidlDownloadEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::ipPayload: {
+ getHidlIpPayloadEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::temi: {
+ getHidlTemiEvent(filterEvents, event);
+ break;
+ }
+ case TunerFilterEvent::monitor: {
+ getHidlMonitorEvent(filterEvents, eventExt);
+ break;
+ }
+ case TunerFilterEvent::startId: {
+ getHidlRestartEvent(filterEvents, eventExt);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void TunerFilterCallback::getHidlMediaEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ hidl_handle handle = hidl_handle(makeFromAidl(filterEvents[i]
+ .get<TunerFilterEvent::media>().avMemory));
+ event.events.resize(i + 1);
+ event.events[i].media({
+ .avMemory = handle,
+ .streamId = static_cast<DemuxStreamId>(filterEvents[i]
+ .get<TunerFilterEvent::media>().streamId),
+ .isPtsPresent = filterEvents[i]
+ .get<TunerFilterEvent::media>().isPtsPresent,
+ .pts = static_cast<uint64_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().pts),
+ .dataLength = static_cast<uint32_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().dataLength),
+ .offset = static_cast<uint32_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().offset),
+ .isSecureMemory = filterEvents[i]
+ .get<TunerFilterEvent::media>().isSecureMemory,
+ .avDataId = static_cast<uint64_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().avDataId),
+ .mpuSequenceNumber = static_cast<uint32_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().offset),
+ .isPesPrivateData = filterEvents[i]
+ .get<TunerFilterEvent::media>().isPesPrivateData,
+ });
+
+ if (filterEvents[i].get<TunerFilterEvent::media>().isAudioExtraMetaData) {
+ event.events[i].media().extraMetaData.audio({
+ .adFade = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adFade),
+ .adPan = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adPan),
+ .versionTextTag = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.versionTextTag),
+ .adGainCenter = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adGainCenter),
+ .adGainFront = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adGainFront),
+ .adGainSurround = static_cast<uint8_t>(filterEvents[i]
+ .get<TunerFilterEvent::media>().audio.adGainSurround),
+ });
+ } else {
+ event.events[i].media().extraMetaData.noinit();
+ }
+ }
+}
+
+void TunerFilterCallback::getHidlSectionEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto section = filterEvents[i].get<TunerFilterEvent::section>();
+ event.events.resize(i + 1);
+ event.events[i].section({
+ .tableId = static_cast<uint16_t>(section.tableId),
+ .version = static_cast<uint16_t>(section.version),
+ .sectionNum = static_cast<uint16_t>(section.sectionNum),
+ .dataLength = static_cast<uint16_t>(section.dataLength),
+ });
+ }
+}
+
+void TunerFilterCallback::getHidlPesEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto pes = filterEvents[i].get<TunerFilterEvent::pes>();
+ event.events.resize(i + 1);
+ event.events[i].pes({
+ .streamId = static_cast<DemuxStreamId>(pes.streamId),
+ .dataLength = static_cast<uint16_t>(pes.dataLength),
+ .mpuSequenceNumber = static_cast<uint32_t>(pes.mpuSequenceNumber),
+ });
+ }
+}
+
+void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto ts = filterEvents[i].get<TunerFilterEvent::tsRecord>();
+ event.events.resize(i + 1);
+ event.events[i].tsRecord({
+ .tsIndexMask = static_cast<uint32_t>(ts.tsIndexMask),
+ .byteNumber = static_cast<uint64_t>(ts.byteNumber),
+ });
+ event.events[i].tsRecord().pid.tPid(static_cast<DemuxTpid>(ts.pid));
+
+ switch (ts.scIndexMask.getTag()) {
+ case TunerFilterScIndexMask::sc: {
+ event.events[i].tsRecord().scIndexMask.sc(
+ ts.scIndexMask.get<TunerFilterScIndexMask::sc>());
+ break;
+ }
+ case TunerFilterScIndexMask::scHevc: {
+ event.events[i].tsRecord().scIndexMask.scHevc(
+ ts.scIndexMask.get<TunerFilterScIndexMask::scHevc>());
+ break;
+ }
+ default:
+ break;
+ }
+
+ eventExt.events.resize(i + 1);
+ if (ts.isExtended) {
+ eventExt.events[i].tsRecord({
+ .pts = static_cast<uint64_t>(ts.pts),
+ .firstMbInSlice = static_cast<uint32_t>(ts.firstMbInSlice),
+ });
+ } else {
+ eventExt.events[i].noinit();
+ }
+ }
+}
+
+void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto mmtp = filterEvents[i].get<TunerFilterEvent::mmtpRecord>();
+ event.events.resize(i + 1);
+ event.events[i].mmtpRecord({
+ .scHevcIndexMask = static_cast<uint32_t>(mmtp.scHevcIndexMask),
+ .byteNumber = static_cast<uint64_t>(mmtp.byteNumber),
+ });
+
+ eventExt.events.resize(i + 1);
+ if (mmtp.isExtended) {
+ eventExt.events[i].mmtpRecord({
+ .pts = static_cast<uint64_t>(mmtp.pts),
+ .mpuSequenceNumber = static_cast<uint32_t>(mmtp.mpuSequenceNumber),
+ .firstMbInSlice = static_cast<uint32_t>(mmtp.firstMbInSlice),
+ .tsIndexMask = static_cast<uint32_t>(mmtp.tsIndexMask),
+ });
+ } else {
+ eventExt.events[i].noinit();
+ }
+ }
+}
+
+void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto download = filterEvents[i].get<TunerFilterEvent::download>();
+ event.events.resize(i + 1);
+ event.events[i].download({
+ .itemId = static_cast<uint32_t>(download.itemId),
+ .mpuSequenceNumber = static_cast<uint32_t>(download.mpuSequenceNumber),
+ .itemFragmentIndex = static_cast<uint32_t>(download.itemFragmentIndex),
+ .lastItemFragmentIndex = static_cast<uint32_t>(download.lastItemFragmentIndex),
+ .dataLength = static_cast<uint16_t>(download.dataLength),
+ });
+ }
+}
+
+void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto ip = filterEvents[i].get<TunerFilterEvent::ipPayload>();
+ event.events.resize(i + 1);
+ event.events[i].ipPayload({
+ .dataLength = static_cast<uint16_t>(ip.dataLength),
+ });
+ }
+}
+
+void TunerFilterCallback::getHidlTemiEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event) {
+ for (int i = 0; i < filterEvents.size(); i++) {
+ auto temi = filterEvents[i].get<TunerFilterEvent::temi>();
+ event.events.resize(i + 1);
+ event.events[i].temi({
+ .pts = static_cast<uint64_t>(temi.pts),
+ .descrTag = static_cast<uint8_t>(temi.descrTag),
+ });
+ vector<uint8_t> descrData(temi.descrData.size());
+ copy(temi.descrData.begin(), temi.descrData.end(), descrData.begin());
+ }
+}
+
+void TunerFilterCallback::getHidlMonitorEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEventExt& eventExt) {
+ auto monitor = filterEvents[0].get<TunerFilterEvent::monitor>();
+ eventExt.events.resize(1);
+ switch (monitor.getTag()) {
+ case TunerFilterMonitorEvent::scramblingStatus: {
+ eventExt.events[0].monitorEvent().scramblingStatus(
+ static_cast<ScramblingStatus>(monitor.scramblingStatus));
+ break;
+ }
+ case TunerFilterMonitorEvent::cid: {
+ eventExt.events[0].monitorEvent().cid(static_cast<uint32_t>(monitor.cid));
+ break;
+ }
+ default:
+ eventExt.events[0].noinit();
+ break;
+ }
+}
+
+void TunerFilterCallback::getHidlRestartEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEventExt& eventExt) {
+ uint32_t startId = filterEvents[0].get<TunerFilterEvent::startId>();
+ eventExt.events.resize(1);
+ eventExt.events[0].startId(static_cast<uint32_t>(startId));
+}
+
Result FilterClient::getFilterMq() {
if (mFilter == NULL) {
return Result::INVALID_STATE;
@@ -333,6 +947,20 @@
}
void FilterClient::handleAvShareMemory() {
+ if (mAvSharedHandle != NULL) {
+ return;
+ }
+
+ if (mTunerFilter != NULL && mIsMediaFilter) {
+ TunerFilterSharedHandleInfo aidlHandleInfo;
+ Status s = mTunerFilter->getAvSharedHandleInfo(&aidlHandleInfo);
+ if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) {
+ mAvSharedHandle = native_handle_clone(makeFromAidl(aidlHandleInfo.handle));
+ mAvSharedMemSize = aidlHandleInfo.size;
+ }
+ return;
+ }
+
if (mFilter_1_1 != NULL && mIsMediaFilter) {
mFilter_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) {
if (r == Result::SUCCESS) {
@@ -342,4 +970,10 @@
});
}
}
+
+void FilterClient::closeAvSharedMemory() {
+ native_handle_close(mAvSharedHandle);
+ native_handle_delete(mAvSharedHandle);
+ mAvSharedMemSize = 0;
+}
} // namespace android
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 7c85125..21919ac 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -20,6 +20,8 @@
#include <aidl/android/media/tv/tuner/ITunerFilter.h>
#include <aidl/android/media/tv/tuner/BnTunerFilterCallback.h>
#include <aidl/android/media/tv/tuner/TunerFilterEvent.h>
+#include <aidl/android/media/tv/tuner/TunerFilterSettings.h>
+#include <aidlcommonsupport/NativeHandle.h>
#include <android/hardware/tv/tuner/1.1/IFilter.h>
#include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
#include <android/hardware/tv/tuner/1.1/types.h>
@@ -31,7 +33,15 @@
using Status = ::ndk::ScopedAStatus;
using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
using ::aidl::android::media::tv::tuner::ITunerFilter;
+using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress;
+using ::aidl::android::media::tv::tuner::TunerFilterAvSettings;
+using ::aidl::android::media::tv::tuner::TunerFilterConfiguration;
+using ::aidl::android::media::tv::tuner::TunerFilterDownloadSettings;
using ::aidl::android::media::tv::tuner::TunerFilterEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterPesDataSettings;
+using ::aidl::android::media::tv::tuner::TunerFilterRecordSettings;
+using ::aidl::android::media::tv::tuner::TunerFilterSectionSettings;
+using ::aidl::android::media::tv::tuner::TunerFilterSettings;
using ::android::hardware::EventFlag;
using ::android::hardware::MessageQueue;
@@ -39,8 +49,19 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_handle;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
using ::android::hardware::tv::tuner::V1_0::IFilter;
using ::android::hardware::tv::tuner::V1_0::Result;
using ::android::hardware::tv::tuner::V1_1::AvStreamType;
@@ -61,11 +82,33 @@
public:
TunerFilterCallback(sp<FilterClientCallback> filterClientCallback);
- // TODO: complete TunerFilterCallback
Status onFilterStatus(int status);
- Status onFilterEvent(vector<TunerFilterEvent>* filterEvent);
+ Status onFilterEvent(const vector<TunerFilterEvent>& filterEvents);
private:
+ void getHidlFilterEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt);
+ void getHidlMediaEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlSectionEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlPesEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt);
+ void getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
+ DemuxFilterEvent& event, DemuxFilterEventExt& eventExt);
+ void getHidlDownloadEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlIpPayloadEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlTemiEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
+ void getHidlMonitorEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEventExt& eventExt);
+ void getHidlRestartEvent(
+ const vector<TunerFilterEvent>& filterEvents, DemuxFilterEventExt& eventExt);
+
sp<FilterClientCallback> mFilterClientCallback;
};
@@ -174,10 +217,27 @@
Result close();
private:
+ TunerFilterConfiguration getAidlFilterSettings(DemuxFilterSettings configure);
+
+ TunerFilterConfiguration getAidlTsSettings(DemuxTsFilterSettings configure);
+ TunerFilterConfiguration getAidlMmtpSettings(DemuxMmtpFilterSettings mmtp);
+ TunerFilterConfiguration getAidlIpSettings(DemuxIpFilterSettings ip);
+ TunerFilterConfiguration getAidlTlvSettings(DemuxTlvFilterSettings tlv);
+ TunerFilterConfiguration getAidlAlpSettings(DemuxAlpFilterSettings alp);
+
+ TunerFilterAvSettings getAidlAvSettings(DemuxFilterAvSettings hidlAv);
+ TunerFilterSectionSettings getAidlSectionSettings(DemuxFilterSectionSettings hidlSection);
+ TunerFilterPesDataSettings getAidlPesDataSettings(DemuxFilterPesDataSettings hidlPesData);
+ TunerFilterRecordSettings getAidlRecordSettings(DemuxFilterRecordSettings hidlRecord);
+ TunerFilterDownloadSettings getAidlDownloadSettings(DemuxFilterDownloadSettings hidlDownload);
+
+ void getAidlIpAddress(DemuxIpAddress ipAddr,
+ TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress);
Result getFilterMq();
int copyData(uint8_t* buffer, int size);
void checkIsMediaFilter(DemuxFilterType type);
void handleAvShareMemory();
+ void closeAvSharedMemory();
/**
* An AIDL Tuner Filter Singleton assigned at the first time when the Tuner Client
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index ef8f57f..08573a6 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -22,7 +22,7 @@
#include "FrontendClient.h"
using ::aidl::android::media::tv::tuner::TunerFrontendScanAtsc3PlpInfo;
-using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendUnionSettings;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
@@ -43,6 +43,7 @@
using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation;
using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
namespace android {
@@ -86,15 +87,13 @@
Result FrontendClient::tune(const FrontendSettings& settings,
const FrontendSettingsExt1_1& settingsExt1_1) {
if (mTunerFrontend != NULL) {
- // TODO: parse hidl settings to aidl settings
- // TODO: aidl frontend settings to include Tuner HAL 1.1 settings
- TunerFrontendSettings settings;
- Status s = mTunerFrontend->tune(settings);
+ TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1);
+ Status s = mTunerFrontend->tune(tunerFeSettings);
return ClientHelper::getServiceSpecificErrorCode(s);
}
Result result;
- if (mFrontend_1_1 != NULL) {
+ if (mFrontend_1_1 != NULL && validateExtendedSettings(settingsExt1_1)) {
result = mFrontend_1_1->tune_1_1(settings, settingsExt1_1);
return result;
}
@@ -124,15 +123,13 @@
Result FrontendClient::scan(const FrontendSettings& settings, FrontendScanType type,
const FrontendSettingsExt1_1& settingsExt1_1) {
if (mTunerFrontend != NULL) {
- // TODO: parse hidl settings to aidl settings
- // TODO: aidl frontend settings to include Tuner HAL 1.1 settings
- TunerFrontendSettings settings;
- Status s = mTunerFrontend->scan(settings, (int)type);
+ TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1);
+ Status s = mTunerFrontend->scan(tunerFeSettings, (int)type);
return ClientHelper::getServiceSpecificErrorCode(s);
}
Result result;
- if (mFrontend_1_1 != NULL) {
+ if (mFrontend_1_1 != NULL && validateExtendedSettings(settingsExt1_1)) {
result = mFrontend_1_1->scan_1_1(settings, type, settingsExt1_1);
return result;
}
@@ -279,7 +276,6 @@
Result FrontendClient::close() {
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
Status s = mTunerFrontend->close();
return ClientHelper::getServiceSpecificErrorCode(s);
}
@@ -296,6 +292,8 @@
return Result::INVALID_STATE;
}
+/////////////// TunerFrontend Helper Methods ///////////////////////
+
shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
return mTunerFrontend;
}
@@ -304,6 +302,254 @@
return mId;
}
+TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSettings& settings,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ bool isExtended = validateExtendedSettings(settingsExt1_1);
+ TunerFrontendSettings s{
+ .isExtended = isExtended,
+ .endFrequency = (int) settingsExt1_1.endFrequency,
+ .inversion = (int) settingsExt1_1.inversion,
+ };
+
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dtmb) {
+ s.settings.set<TunerFrontendUnionSettings::dtmb>(getAidlDtmbSettings(settingsExt1_1));
+ return s;
+ }
+
+ switch (settings.getDiscriminator()) {
+ case FrontendSettings::hidl_discriminator::analog: {
+ s.settings.set<TunerFrontendUnionSettings::analog>(
+ getAidlAnalogSettings(settings, settingsExt1_1));
+ break;
+ }
+ case FrontendSettings::hidl_discriminator::atsc: {
+ s.settings.set<TunerFrontendUnionSettings::atsc>(getAidlAtscSettings(settings));
+ break;
+ }
+ case FrontendSettings::hidl_discriminator::atsc3: {
+ s.settings.set<TunerFrontendUnionSettings::atsc3>(getAidlAtsc3Settings(settings));
+ break;
+ }
+ case FrontendSettings::hidl_discriminator::dvbs: {
+ s.settings.set<TunerFrontendUnionSettings::dvbs>(
+ getAidlDvbsSettings(settings, settingsExt1_1));
+ break;
+ }
+ case FrontendSettings::hidl_discriminator::dvbc: {
+ s.settings.set<TunerFrontendUnionSettings::cable>(
+ getAidlCableSettings(settings, settingsExt1_1));
+ break;
+ }
+ case FrontendSettings::hidl_discriminator::dvbt: {
+ s.settings.set<TunerFrontendUnionSettings::dvbt>(
+ getAidlDvbtSettings(settings, settingsExt1_1));
+ break;
+ }
+ case FrontendSettings::hidl_discriminator::isdbs: {
+ s.settings.set<TunerFrontendUnionSettings::isdbs>(getAidlIsdbsSettings(settings));
+ break;
+ }
+ case FrontendSettings::hidl_discriminator::isdbs3: {
+ s.settings.set<TunerFrontendUnionSettings::isdbs3>(getAidlIsdbs3Settings(settings));
+ break;
+ }
+ case FrontendSettings::hidl_discriminator::isdbt: {
+ s.settings.set<TunerFrontendUnionSettings::isdbt>(getAidlIsdbtSettings(settings));
+ break;
+ }
+ default:
+ break;
+ }
+ return s;
+}
+
+TunerFrontendAnalogSettings FrontendClient::getAidlAnalogSettings(const FrontendSettings& settings,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendAnalogSettings analogSettings{
+ .frequency = (int)settings.analog().frequency,
+ .signalType = (int)settings.analog().type,
+ .sifStandard = (int)settings.analog().sifStandard,
+ };
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::analog) {
+ analogSettings.isExtended = true;
+ analogSettings.aftFlag = (int)settingsExt1_1.settingExt.analog().aftFlag;
+ } else {
+ analogSettings.isExtended = false;
+ }
+ return analogSettings;
+}
+
+TunerFrontendDvbsSettings FrontendClient::getAidlDvbsSettings(const FrontendSettings& settings,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendDvbsSettings dvbsSettings{
+ .frequency = (int)settings.dvbs().frequency,
+ .modulation = (int)settings.dvbs().modulation,
+ .codeRate = {
+ .fec = (long)settings.dvbs().coderate.fec,
+ .isLinear = settings.dvbs().coderate.isLinear,
+ .isShortFrames = settings.dvbs().coderate.isShortFrames,
+ .bitsPer1000Symbol = (int)settings.dvbs().coderate.bitsPer1000Symbol,
+ },
+ .symbolRate = (int)settings.dvbs().symbolRate,
+ .rolloff = (int)settings.dvbs().rolloff,
+ .pilot = (int)settings.dvbs().pilot,
+ .inputStreamId = (int)settings.dvbs().inputStreamId,
+ .standard = (int)settings.dvbs().standard,
+ .vcm = (int)settings.dvbs().vcmMode,
+ };
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbs) {
+ dvbsSettings.isExtended = true;
+ dvbsSettings.scanType = (int)settingsExt1_1.settingExt.dvbs().scanType;
+ dvbsSettings.isDiseqcRxMessage = settingsExt1_1.settingExt.dvbs().isDiseqcRxMessage;
+ } else {
+ dvbsSettings.isExtended = false;
+ }
+ return dvbsSettings;
+}
+
+TunerFrontendCableSettings FrontendClient::getAidlCableSettings(const FrontendSettings& settings,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendCableSettings cableSettings{
+ .frequency = (int)settings.dvbc().frequency,
+ .modulation = (int)settings.dvbc().modulation,
+ .innerFec = (long)settings.dvbc().fec,
+ .symbolRate = (int)settings.dvbc().symbolRate,
+ .outerFec = (int)settings.dvbc().outerFec,
+ .annex = (int)settings.dvbc().annex,
+ .spectralInversion = (int)settings.dvbc().spectralInversion,
+ };
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbc) {
+ cableSettings.isExtended = true;
+ cableSettings.interleaveMode = (int)settingsExt1_1.settingExt.dvbc().interleaveMode;
+ cableSettings.bandwidth = (int)settingsExt1_1.settingExt.dvbc().bandwidth;
+ } else {
+ cableSettings.isExtended = false;
+ }
+ return cableSettings;
+}
+
+TunerFrontendDvbtSettings FrontendClient::getAidlDvbtSettings(const FrontendSettings& settings,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendDvbtSettings dvbtSettings{
+ .frequency = (int)settings.dvbt().frequency,
+ .transmissionMode = (int)settings.dvbt().transmissionMode,
+ .bandwidth = (int)settings.dvbt().bandwidth,
+ .constellation = (int)settings.dvbt().constellation,
+ .hierarchy = (int)settings.dvbt().hierarchy,
+ .hpCodeRate = (int)settings.dvbt().hpCoderate,
+ .lpCodeRate = (int)settings.dvbt().lpCoderate,
+ .guardInterval = (int)settings.dvbt().guardInterval,
+ .isHighPriority = settings.dvbt().isHighPriority,
+ .standard = (int)settings.dvbt().standard,
+ .isMiso = settings.dvbt().isMiso,
+ .plpMode = (int)settings.dvbt().plpMode,
+ .plpId = (int)settings.dvbt().plpId,
+ .plpGroupId = (int)settings.dvbt().plpGroupId,
+ };
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbt) {
+ dvbtSettings.isExtended = true;
+ dvbtSettings.constellation = (int)settingsExt1_1.settingExt.dvbt().constellation;
+ dvbtSettings.transmissionMode =
+ (int)settingsExt1_1.settingExt.dvbt().transmissionMode;
+ } else {
+ dvbtSettings.isExtended = false;
+ }
+ return dvbtSettings;
+}
+
+TunerFrontendDtmbSettings FrontendClient::getAidlDtmbSettings(
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendDtmbSettings dtmbSettings{
+ .frequency = (int)settingsExt1_1.settingExt.dtmb().frequency,
+ .transmissionMode = (int)settingsExt1_1.settingExt.dtmb().transmissionMode,
+ .bandwidth = (int)settingsExt1_1.settingExt.dtmb().bandwidth,
+ .modulation = (int)settingsExt1_1.settingExt.dtmb().modulation,
+ .codeRate = (int)settingsExt1_1.settingExt.dtmb().codeRate,
+ .guardInterval = (int)settingsExt1_1.settingExt.dtmb().guardInterval,
+ .interleaveMode = (int)settingsExt1_1.settingExt.dtmb().interleaveMode,
+ };
+ return dtmbSettings;
+}
+
+TunerFrontendAtscSettings FrontendClient::getAidlAtscSettings(const FrontendSettings& settings) {
+ TunerFrontendAtscSettings atscSettings{
+ .frequency = (int)settings.atsc().frequency,
+ .modulation = (int)settings.atsc().modulation,
+ };
+ return atscSettings;
+}
+
+TunerFrontendAtsc3Settings FrontendClient::getAidlAtsc3Settings(const FrontendSettings& settings) {
+ TunerFrontendAtsc3Settings atsc3Settings{
+ .frequency = (int)settings.atsc3().frequency,
+ .bandwidth = (int)settings.atsc3().bandwidth,
+ .demodOutputFormat = (int)settings.atsc3().demodOutputFormat,
+ };
+ atsc3Settings.plpSettings.resize(settings.atsc3().plpSettings.size());
+ for (auto plpSetting : settings.atsc3().plpSettings) {
+ atsc3Settings.plpSettings.push_back({
+ .plpId = (int)plpSetting.plpId,
+ .modulation = (int)plpSetting.modulation,
+ .interleaveMode = (int)plpSetting.interleaveMode,
+ .codeRate = (int)plpSetting.codeRate,
+ .fec = (int)plpSetting.fec,
+ });
+ }
+ return atsc3Settings;
+}
+
+TunerFrontendIsdbsSettings FrontendClient::getAidlIsdbsSettings(const FrontendSettings& settings) {
+ TunerFrontendIsdbsSettings isdbsSettings{
+ .frequency = (int)settings.isdbs().frequency,
+ .streamId = (int)settings.isdbs().streamId,
+ .streamIdType = (int)settings.isdbs().streamIdType,
+ .modulation = (int)settings.isdbs().modulation,
+ .codeRate = (int)settings.isdbs().coderate,
+ .symbolRate = (int)settings.isdbs().symbolRate,
+ .rolloff = (int)settings.isdbs().rolloff,
+ };
+ return isdbsSettings;
+}
+
+TunerFrontendIsdbs3Settings FrontendClient::getAidlIsdbs3Settings(
+ const FrontendSettings& settings) {
+ TunerFrontendIsdbs3Settings isdbs3Settings{
+ .frequency = (int)settings.isdbs3().frequency,
+ .streamId = (int)settings.isdbs3().streamId,
+ .streamIdType = (int)settings.isdbs3().streamIdType,
+ .modulation = (int)settings.isdbs3().modulation,
+ .codeRate = (int)settings.isdbs3().coderate,
+ .symbolRate = (int)settings.isdbs3().symbolRate,
+ .rolloff = (int)settings.isdbs3().rolloff,
+ };
+ return isdbs3Settings;
+}
+
+TunerFrontendIsdbtSettings FrontendClient::getAidlIsdbtSettings(const FrontendSettings& settings) {
+ TunerFrontendIsdbtSettings isdbtSettings{
+ .frequency = (int)settings.isdbt().frequency,
+ .modulation = (int)settings.isdbt().modulation,
+ .bandwidth = (int)settings.isdbt().bandwidth,
+ .mode = (int)settings.isdbt().mode,
+ .codeRate = (int)settings.isdbt().coderate,
+ .guardInterval = (int)settings.isdbt().guardInterval,
+ .serviceAreaId = (int)settings.isdbt().serviceAreaId,
+ };
+ return isdbtSettings;
+}
+
+bool FrontendClient::validateExtendedSettings(const FrontendSettingsExt1_1& settingsExt1_1) {
+ return settingsExt1_1.endFrequency != (uint32_t)Constant::INVALID_FRONTEND_SETTING_FREQUENCY
+ || settingsExt1_1.inversion != FrontendSpectralInversion::UNDEFINED
+ || settingsExt1_1.settingExt.getDiscriminator()
+ != FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::noinit;
+}
+
/////////////// TunerFrontendCallback ///////////////////////
TunerFrontendCallback::TunerFrontendCallback(sp<FrontendClientCallback> frontendClientCallback)
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 03ebb87..b0107ff 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -31,7 +31,18 @@
using ::aidl::android::media::tv::tuner::BnTunerFrontendCallback;
using ::aidl::android::media::tv::tuner::ITunerFrontend;
+using ::aidl::android::media::tv::tuner::TunerFrontendAnalogSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendAtscSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendAtsc3Settings;
+using ::aidl::android::media::tv::tuner::TunerFrontendCableSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDvbsSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDvbtSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDtmbSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbsSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbs3Settings;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtSettings;
using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage;
+using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -171,6 +182,25 @@
int getId();
private:
+ TunerFrontendSettings getAidlFrontendSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendAnalogSettings getAidlAnalogSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendDvbsSettings getAidlDvbsSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendCableSettings getAidlCableSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendDvbtSettings getAidlDvbtSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendDtmbSettings getAidlDtmbSettings(const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendAtscSettings getAidlAtscSettings(const FrontendSettings& settings);
+ TunerFrontendAtsc3Settings getAidlAtsc3Settings(const FrontendSettings& settings);
+ TunerFrontendIsdbsSettings getAidlIsdbsSettings(const FrontendSettings& settings);
+ TunerFrontendIsdbs3Settings getAidlIsdbs3Settings(const FrontendSettings& settings);
+ TunerFrontendIsdbtSettings getAidlIsdbtSettings(const FrontendSettings& settings);
+
+ bool validateExtendedSettings(const FrontendSettingsExt1_1& settingsExt1_1);
+
/**
* An AIDL Tuner Frontend Singleton assigned at the first time when the Tuner Client
* opens a frontend cient. Default null when the service does not exist.
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 4498f54..14393a1 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -235,7 +235,8 @@
}
}
- return NULL;}
+ return NULL;
+}
sp<LnbClient> TunerClient::openLnb(int lnbHandle) {
if (mTunerService != NULL) {
diff --git a/media/native/midi/include/amidi/AMidi.h b/media/native/midi/include/amidi/AMidi.h
index 0f930b5..742db34 100644
--- a/media/native/midi/include/amidi/AMidi.h
+++ b/media/native/midi/include/amidi/AMidi.h
@@ -61,8 +61,6 @@
AMIDI_DEVICE_TYPE_BLUETOOTH = 3 /* A MIDI device connected via BlueTooth */
};
-#if __ANDROID_API__ >= 29
-
/*
* Device API
*/
@@ -249,8 +247,6 @@
*/
void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
-#endif /* __ANDROID_API__ >= 29 */
-
#ifdef __cplusplus
}
#endif
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 1286fc1..4b0062b 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -256,7 +256,7 @@
CountDownLatch latch = new CountDownLatch(1);
- addManagerCallback(new MediaRouter2Manager.Callback());
+ addManagerCallback(new MediaRouter2Manager.Callback() {});
addRouterCallback(new MediaRouter2.RouteCallback() {});
addTransferCallback(new MediaRouter2.TransferCallback() {
@Override
@@ -530,7 +530,7 @@
@Test
public void testSetSystemRouteVolume() throws Exception {
// ensure client
- addManagerCallback(new MediaRouter2Manager.Callback());
+ addManagerCallback(new MediaRouter2Manager.Callback() {});
String selectedSystemRouteId =
MediaRouter2Utils.getOriginalId(
mManager.getActiveSessions().get(0).getSelectedRoutes().get(0));
@@ -902,7 +902,7 @@
private void releaseAllSessions() {
// ensure ManagerRecord in MediaRouter2ServiceImpl
- addManagerCallback(new MediaRouter2Manager.Callback());
+ addManagerCallback(new MediaRouter2Manager.Callback() {});
for (RoutingSessionInfo session : mManager.getActiveSessions()) {
mManager.releaseSession(session);
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index d464587..3d633ea 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -27,14 +27,13 @@
],
shared_libs: [
- "libandroid_runtime",
"libhwui",
"liblog",
],
header_libs: [
- "libhwui_internal_headers",
"jni_headers",
+ "libhwui_internal_headers",
],
static_libs: ["libarect"],
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 620c7ae..72589e3 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -18,6 +18,7 @@
import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
import static android.text.TextUtils.emptyIfNull;
+import static android.text.TextUtils.isEmpty;
import static android.text.TextUtils.withoutPrefix;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -67,7 +68,13 @@
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
String deviceProfile = getRequest().getDeviceProfile();
- String profileName = getDeviceProfileName(deviceProfile);
+ String profilePrivacyDisclaimer = emptyIfNull(getRequest()
+ .getDeviceProfilePrivilegesDescription())
+ .replace("APP_NAME", getCallingAppName());
+ boolean useDeviceProfile = deviceProfile != null && !isEmpty(profilePrivacyDisclaimer);
+ String profileName = useDeviceProfile
+ ? getDeviceProfileName(deviceProfile)
+ : getString(R.string.profile_name_generic);
if (getRequest().isSingleDevice()) {
setContentView(R.layout.device_confirmation);
@@ -110,15 +117,12 @@
TextView profileSummary = findViewById(R.id.profile_summary);
- if (deviceProfile != null) {
- String privacyDisclaimer = emptyIfNull(getRequest()
- .getDeviceProfilePrivilegesDescription())
- .replace("APP_NAME", getCallingAppName());
+ if (useDeviceProfile) {
profileSummary.setVisibility(View.VISIBLE);
profileSummary.setText(getString(R.string.profile_summary,
getCallingAppName(),
profileName,
- privacyDisclaimer));
+ profilePrivacyDisclaimer));
} else {
profileSummary.setVisibility(View.GONE);
}
@@ -142,7 +146,7 @@
return getString(R.string.profile_name_watch);
}
default: {
- Log.wtf(LOG_TAG,
+ Log.w(LOG_TAG,
"No localized profile name found for device profile: " + deviceProfile);
return withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile)
.toLowerCase()
diff --git a/core/java/android/net/CaptivePortal.java b/packages/Connectivity/framework/src/android/net/CaptivePortal.java
similarity index 100%
rename from core/java/android/net/CaptivePortal.java
rename to packages/Connectivity/framework/src/android/net/CaptivePortal.java
diff --git a/core/java/android/net/CaptivePortalData.aidl b/packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl
similarity index 100%
rename from core/java/android/net/CaptivePortalData.aidl
rename to packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl
diff --git a/core/java/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
similarity index 100%
rename from core/java/android/net/CaptivePortalData.java
rename to packages/Connectivity/framework/src/android/net/CaptivePortalData.java
diff --git a/core/java/android/net/ConnectionInfo.aidl b/packages/Connectivity/framework/src/android/net/ConnectionInfo.aidl
similarity index 100%
rename from core/java/android/net/ConnectionInfo.aidl
rename to packages/Connectivity/framework/src/android/net/ConnectionInfo.aidl
diff --git a/core/java/android/net/ConnectionInfo.java b/packages/Connectivity/framework/src/android/net/ConnectionInfo.java
similarity index 100%
rename from core/java/android/net/ConnectionInfo.java
rename to packages/Connectivity/framework/src/android/net/ConnectionInfo.java
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.aidl b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl
similarity index 100%
rename from core/java/android/net/ConnectivityDiagnosticsManager.aidl
rename to packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
similarity index 100%
rename from core/java/android/net/ConnectivityDiagnosticsManager.java
rename to packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
new file mode 100644
index 0000000..9afa5d1
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for performing registration for all core connectivity services.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ConnectivityFrameworkInitializer {
+ private ConnectivityFrameworkInitializer() {}
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all core
+ * connectivity services to {@link Context}, so that {@link Context#getSystemService} can
+ * return them.
+ *
+ * @throws IllegalStateException if this is called anywhere besides
+ * {@link SystemServiceRegistry}.
+ */
+ public static void registerServiceWrappers() {
+ // registerContextAwareService will throw if this is called outside of SystemServiceRegistry
+ // initialization.
+ SystemServiceRegistry.registerContextAwareService(
+ Context.CONNECTIVITY_SERVICE,
+ ConnectivityManager.class,
+ (context, serviceBinder) -> {
+ IConnectivityManager icm = IConnectivityManager.Stub.asInterface(serviceBinder);
+ return new ConnectivityManager(context, icm);
+ }
+ );
+
+ // TODO: move outside of the connectivity JAR
+ SystemServiceRegistry.registerContextAwareService(
+ Context.VPN_MANAGEMENT_SERVICE,
+ VpnManager.class,
+ (context) -> {
+ final ConnectivityManager cm = context.getSystemService(
+ ConnectivityManager.class);
+ return cm.createVpnManager();
+ }
+ );
+
+ SystemServiceRegistry.registerContextAwareService(
+ Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ ConnectivityDiagnosticsManager.class,
+ (context) -> {
+ final ConnectivityManager cm = context.getSystemService(
+ ConnectivityManager.class);
+ return cm.createDiagnosticsManager();
+ }
+ );
+
+ SystemServiceRegistry.registerContextAwareService(
+ Context.TEST_NETWORK_SERVICE,
+ TestNetworkManager.class,
+ context -> {
+ final ConnectivityManager cm = context.getSystemService(
+ ConnectivityManager.class);
+ return cm.startOrGetTestNetworkManager();
+ }
+ );
+ }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
similarity index 96%
rename from core/java/android/net/ConnectivityManager.java
rename to packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 2e45ed8..2716e09 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -15,7 +15,9 @@
*/
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
import static android.net.NetworkRequest.Type.LISTEN;
import static android.net.NetworkRequest.Type.REQUEST;
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
@@ -28,6 +30,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
@@ -4606,7 +4609,7 @@
// Set HTTP proxy system properties to match network.
// TODO: Deprecate this static method and replace it with a non-static version.
try {
- Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
+ Proxy.setHttpProxyConfiguration(getInstance().getDefaultProxy());
} catch (SecurityException e) {
// The process doesn't have ACCESS_NETWORK_STATE, so we can't fetch the proxy.
Log.e(TAG, "Can't set proxy properties", e);
@@ -4820,6 +4823,28 @@
}
}
+ /** @hide */
+ public TestNetworkManager startOrGetTestNetworkManager() {
+ final IBinder tnBinder;
+ try {
+ tnBinder = mService.startOrGetTestNetworkService();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder));
+ }
+
+ /** @hide */
+ public VpnManager createVpnManager() {
+ return new VpnManager(mContext, mService);
+ }
+
+ /** @hide */
+ public ConnectivityDiagnosticsManager createDiagnosticsManager() {
+ return new ConnectivityDiagnosticsManager(mContext, mService);
+ }
+
/**
* Simulates a Data Stall for the specified Network.
*
@@ -4964,4 +4989,92 @@
}
return null;
}
+
+ /**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but
+ * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can
+ * be used to request that the system provide a network without causing the network to be
+ * in the foreground.
+ *
+ * <p>This method will attempt to find the best network that matches the passed
+ * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
+ * criteria. The platform will evaluate which network is the best at its own discretion.
+ * Throughput, latency, cost per byte, policy, user preference and other considerations
+ * may be factored in the decision of what is considered the best network.
+ *
+ * <p>As long as this request is outstanding, the platform will try to maintain the best network
+ * matching this request, while always attempting to match the request to a better network if
+ * possible. If a better match is found, the platform will switch this request to the now-best
+ * network and inform the app of the newly best network by invoking
+ * {@link NetworkCallback#onAvailable(Network)} on the provided callback. Note that the platform
+ * will not try to maintain any other network than the best one currently matching the request:
+ * a network not matching any network request may be disconnected at any time.
+ *
+ * <p>For example, an application could use this method to obtain a connected cellular network
+ * even if the device currently has a data connection over Ethernet. This may cause the cellular
+ * radio to consume additional power. Or, an application could inform the system that it wants
+ * a network supporting sending MMSes and have the system let it know about the currently best
+ * MMS-supporting network through the provided {@link NetworkCallback}.
+ *
+ * <p>The status of the request can be followed by listening to the various callbacks described
+ * in {@link NetworkCallback}. The {@link Network} object passed to the callback methods can be
+ * used to direct traffic to the network (although accessing some networks may be subject to
+ * holding specific permissions). Callers will learn about the specific characteristics of the
+ * network through
+ * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)} and
+ * {@link NetworkCallback#onLinkPropertiesChanged(Network, LinkProperties)}. The methods of the
+ * provided {@link NetworkCallback} will only be invoked due to changes in the best network
+ * matching the request at any given time; therefore when a better network matching the request
+ * becomes available, the {@link NetworkCallback#onAvailable(Network)} method is called
+ * with the new network after which no further updates are given about the previously-best
+ * network, unless it becomes the best again at some later time. All callbacks are invoked
+ * in order on the same thread, which by default is a thread created by the framework running
+ * in the app.
+ *
+ * <p>This{@link NetworkRequest} will live until released via
+ * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits, at
+ * which point the system may let go of the network at any time.
+ *
+ * <p>It is presently unsupported to request a network with mutable
+ * {@link NetworkCapabilities} such as
+ * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
+ * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}
+ * as these {@code NetworkCapabilities} represent states that a particular
+ * network may never attain, and whether a network will attain these states
+ * is unknown prior to bringing up the network so the framework does not
+ * know how to go about satisfying a request with these capabilities.
+ *
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * all variants of this method, of {@link #registerNetworkCallback} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with
+ * {@link #unregisterNetworkCallback(NetworkCallback)}.
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * If null, the callback is invoked on the default internal Handler.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
+ * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
+ * @throws SecurityException if missing the appropriate permissions.
+ * @throws RuntimeException if the app already has too many callbacks registered.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @SuppressLint("ExecutorRegistration")
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void requestBackgroundNetwork(@NonNull NetworkRequest request,
+ @Nullable Handler handler, @NonNull NetworkCallback networkCallback) {
+ final NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST,
+ TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler));
+ }
}
diff --git a/core/java/android/net/ConnectivityMetricsEvent.aidl b/packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl
similarity index 100%
rename from core/java/android/net/ConnectivityMetricsEvent.aidl
rename to packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl
diff --git a/core/java/android/net/ConnectivityThread.java b/packages/Connectivity/framework/src/android/net/ConnectivityThread.java
similarity index 100%
rename from core/java/android/net/ConnectivityThread.java
rename to packages/Connectivity/framework/src/android/net/ConnectivityThread.java
diff --git a/core/java/android/net/DhcpInfo.aidl b/packages/Connectivity/framework/src/android/net/DhcpInfo.aidl
similarity index 100%
rename from core/java/android/net/DhcpInfo.aidl
rename to packages/Connectivity/framework/src/android/net/DhcpInfo.aidl
diff --git a/core/java/android/net/DhcpInfo.java b/packages/Connectivity/framework/src/android/net/DhcpInfo.java
similarity index 100%
rename from core/java/android/net/DhcpInfo.java
rename to packages/Connectivity/framework/src/android/net/DhcpInfo.java
diff --git a/core/java/android/net/DnsResolver.java b/packages/Connectivity/framework/src/android/net/DnsResolver.java
similarity index 100%
rename from core/java/android/net/DnsResolver.java
rename to packages/Connectivity/framework/src/android/net/DnsResolver.java
diff --git a/core/java/android/net/ICaptivePortal.aidl b/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
similarity index 100%
rename from core/java/android/net/ICaptivePortal.aidl
rename to packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
diff --git a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityDiagnosticsCallback.aidl
similarity index 100%
rename from core/java/android/net/IConnectivityDiagnosticsCallback.aidl
rename to packages/Connectivity/framework/src/android/net/IConnectivityDiagnosticsCallback.aidl
diff --git a/core/java/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
similarity index 99%
rename from core/java/android/net/IConnectivityManager.aidl
rename to packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 7197831..1b4d2e4 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -31,7 +31,6 @@
import android.net.NetworkState;
import android.net.ProxyInfo;
import android.net.UidRange;
-import android.net.VpnInfo;
import android.net.QosSocketInfo;
import android.os.Bundle;
import android.os.IBinder;
diff --git a/core/java/android/net/ISocketKeepaliveCallback.aidl b/packages/Connectivity/framework/src/android/net/ISocketKeepaliveCallback.aidl
similarity index 100%
rename from core/java/android/net/ISocketKeepaliveCallback.aidl
rename to packages/Connectivity/framework/src/android/net/ISocketKeepaliveCallback.aidl
diff --git a/core/java/android/net/ITestNetworkManager.aidl b/packages/Connectivity/framework/src/android/net/ITestNetworkManager.aidl
similarity index 100%
rename from core/java/android/net/ITestNetworkManager.aidl
rename to packages/Connectivity/framework/src/android/net/ITestNetworkManager.aidl
diff --git a/core/java/android/net/InetAddresses.java b/packages/Connectivity/framework/src/android/net/InetAddresses.java
similarity index 100%
rename from core/java/android/net/InetAddresses.java
rename to packages/Connectivity/framework/src/android/net/InetAddresses.java
diff --git a/core/java/android/net/InterfaceConfiguration.aidl b/packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl
similarity index 100%
rename from core/java/android/net/InterfaceConfiguration.aidl
rename to packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl
diff --git a/core/java/android/net/InvalidPacketException.java b/packages/Connectivity/framework/src/android/net/InvalidPacketException.java
similarity index 100%
rename from core/java/android/net/InvalidPacketException.java
rename to packages/Connectivity/framework/src/android/net/InvalidPacketException.java
diff --git a/core/java/android/net/IpConfiguration.aidl b/packages/Connectivity/framework/src/android/net/IpConfiguration.aidl
similarity index 100%
rename from core/java/android/net/IpConfiguration.aidl
rename to packages/Connectivity/framework/src/android/net/IpConfiguration.aidl
diff --git a/core/java/android/net/IpConfiguration.java b/packages/Connectivity/framework/src/android/net/IpConfiguration.java
similarity index 100%
rename from core/java/android/net/IpConfiguration.java
rename to packages/Connectivity/framework/src/android/net/IpConfiguration.java
diff --git a/core/java/android/net/IpPrefix.aidl b/packages/Connectivity/framework/src/android/net/IpPrefix.aidl
similarity index 100%
rename from core/java/android/net/IpPrefix.aidl
rename to packages/Connectivity/framework/src/android/net/IpPrefix.aidl
diff --git a/core/java/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java
similarity index 100%
rename from core/java/android/net/IpPrefix.java
rename to packages/Connectivity/framework/src/android/net/IpPrefix.java
diff --git a/core/java/android/net/KeepalivePacketData.aidl b/packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl
similarity index 100%
rename from core/java/android/net/KeepalivePacketData.aidl
rename to packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl
diff --git a/core/java/android/net/KeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/KeepalivePacketData.java
similarity index 100%
rename from core/java/android/net/KeepalivePacketData.java
rename to packages/Connectivity/framework/src/android/net/KeepalivePacketData.java
diff --git a/core/java/android/net/LinkAddress.aidl b/packages/Connectivity/framework/src/android/net/LinkAddress.aidl
similarity index 100%
rename from core/java/android/net/LinkAddress.aidl
rename to packages/Connectivity/framework/src/android/net/LinkAddress.aidl
diff --git a/core/java/android/net/LinkAddress.java b/packages/Connectivity/framework/src/android/net/LinkAddress.java
similarity index 100%
rename from core/java/android/net/LinkAddress.java
rename to packages/Connectivity/framework/src/android/net/LinkAddress.java
diff --git a/core/java/android/net/LinkProperties.aidl b/packages/Connectivity/framework/src/android/net/LinkProperties.aidl
similarity index 100%
rename from core/java/android/net/LinkProperties.aidl
rename to packages/Connectivity/framework/src/android/net/LinkProperties.aidl
diff --git a/core/java/android/net/LinkProperties.java b/packages/Connectivity/framework/src/android/net/LinkProperties.java
similarity index 100%
rename from core/java/android/net/LinkProperties.java
rename to packages/Connectivity/framework/src/android/net/LinkProperties.java
diff --git a/core/java/android/net/MacAddress.aidl b/packages/Connectivity/framework/src/android/net/MacAddress.aidl
similarity index 100%
rename from core/java/android/net/MacAddress.aidl
rename to packages/Connectivity/framework/src/android/net/MacAddress.aidl
diff --git a/core/java/android/net/MacAddress.java b/packages/Connectivity/framework/src/android/net/MacAddress.java
similarity index 100%
rename from core/java/android/net/MacAddress.java
rename to packages/Connectivity/framework/src/android/net/MacAddress.java
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/NattKeepalivePacketData.java
similarity index 100%
rename from core/java/android/net/NattKeepalivePacketData.java
rename to packages/Connectivity/framework/src/android/net/NattKeepalivePacketData.java
diff --git a/core/java/android/net/NattSocketKeepalive.java b/packages/Connectivity/framework/src/android/net/NattSocketKeepalive.java
similarity index 100%
rename from core/java/android/net/NattSocketKeepalive.java
rename to packages/Connectivity/framework/src/android/net/NattSocketKeepalive.java
diff --git a/core/java/android/net/Network.aidl b/packages/Connectivity/framework/src/android/net/Network.aidl
similarity index 100%
rename from core/java/android/net/Network.aidl
rename to packages/Connectivity/framework/src/android/net/Network.aidl
diff --git a/core/java/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java
similarity index 100%
rename from core/java/android/net/Network.java
rename to packages/Connectivity/framework/src/android/net/Network.java
diff --git a/core/java/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
similarity index 99%
rename from core/java/android/net/NetworkAgent.java
rename to packages/Connectivity/framework/src/android/net/NetworkAgent.java
index d22d82d..27aa15d 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -775,7 +776,8 @@
* @param underlyingNetworks the new list of underlying networks.
* @see {@link VpnService.Builder#setUnderlyingNetworks(Network[])}
*/
- public final void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) {
+ public final void setUnderlyingNetworks(
+ @SuppressLint("NullableCollection") @Nullable List<Network> underlyingNetworks) {
final ArrayList<Network> underlyingArray = (underlyingNetworks != null)
? new ArrayList<>(underlyingNetworks) : null;
queueOrSendMessage(reg -> reg.sendUnderlyingNetworks(underlyingArray));
diff --git a/core/java/android/net/NetworkAgentConfig.aidl b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl
similarity index 100%
rename from core/java/android/net/NetworkAgentConfig.aidl
rename to packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl
diff --git a/core/java/android/net/NetworkAgentConfig.java b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
similarity index 98%
rename from core/java/android/net/NetworkAgentConfig.java
rename to packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
index fe1268d..664c265 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -125,6 +127,7 @@
* @return the subscriber ID, or null if none.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
@Nullable
public String getSubscriberId() {
return subscriberId;
@@ -275,6 +278,7 @@
* @hide
*/
@NonNull
+ @SystemApi(client = MODULE_LIBRARIES)
public Builder setSubscriberId(@Nullable String subscriberId) {
mConfig.subscriberId = subscriberId;
return this;
diff --git a/core/java/android/net/NetworkCapabilities.aidl b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl
similarity index 100%
rename from core/java/android/net/NetworkCapabilities.aidl
rename to packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl
diff --git a/core/java/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
similarity index 97%
rename from core/java/android/net/NetworkCapabilities.java
rename to packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 3843b9a..55b2c3c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -34,9 +34,9 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
import com.android.internal.util.Preconditions;
+import com.android.net.module.util.CollectionUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -401,11 +401,18 @@
public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
/**
- * Indicates that this network is not managed by a Virtual Carrier Network (VCN).
- *
- * TODO(b/177299683): Add additional clarifying javadoc.
+ * Indicates that this network is not subsumed by a Virtual Carrier Network (VCN).
+ * <p>
+ * To provide an experience on a VCN similar to a single traditional carrier network, in
+ * some cases the system sets this bit is set by default in application's network requests,
+ * and may choose to remove it at its own discretion when matching the request to a network.
+ * <p>
+ * Applications that want to know about a Virtual Carrier Network's underlying networks,
+ * for example to use them for multipath purposes, should remove this bit from their network
+ * requests ; the system will not add it back once removed.
* @hide
*/
+ @SystemApi
public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
@@ -767,7 +774,7 @@
if (originalOwnerUid == creatorUid) {
setOwnerUid(creatorUid);
}
- if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) {
+ if (CollectionUtils.contains(originalAdministratorUids, creatorUid)) {
setAdministratorUids(new int[] {creatorUid});
}
// There is no need to clear the UIDs, they have already been cleared by clearAll() above.
@@ -1779,6 +1786,15 @@
return 0;
}
+ private <T extends Parcelable> void writeParcelableArraySet(Parcel in,
+ @Nullable ArraySet<T> val, int flags) {
+ final int size = (val != null) ? val.size() : -1;
+ in.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ in.writeParcelable(val.valueAt(i), flags);
+ }
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mNetworkCapabilities);
@@ -1789,7 +1805,7 @@
dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
dest.writeParcelable((Parcelable) mTransportInfo, flags);
dest.writeInt(mSignalStrength);
- dest.writeArraySet(mUids);
+ writeParcelableArraySet(dest, mUids, flags);
dest.writeString(mSSID);
dest.writeBoolean(mPrivateDnsBroken);
dest.writeIntArray(getAdministratorUids());
@@ -1812,8 +1828,7 @@
netCap.mNetworkSpecifier = in.readParcelable(null);
netCap.mTransportInfo = in.readParcelable(null);
netCap.mSignalStrength = in.readInt();
- netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
- null /* ClassLoader, null for default */);
+ netCap.mUids = readParcelableArraySet(in, null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
netCap.mPrivateDnsBroken = in.readBoolean();
netCap.setAdministratorUids(in.createIntArray());
@@ -1826,6 +1841,20 @@
public NetworkCapabilities[] newArray(int size) {
return new NetworkCapabilities[size];
}
+
+ private @Nullable <T extends Parcelable> ArraySet<T> readParcelableArraySet(Parcel in,
+ @Nullable ClassLoader loader) {
+ final int size = in.readInt();
+ if (size < 0) {
+ return null;
+ }
+ final ArraySet<T> result = new ArraySet<>(size);
+ for (int i = 0; i < size; i++) {
+ final T value = in.readParcelable(loader);
+ result.append(value);
+ }
+ return result;
+ }
};
@Override
@@ -1873,7 +1902,7 @@
sb.append(" OwnerUid: ").append(mOwnerUid);
}
- if (!ArrayUtils.isEmpty(mAdministratorUids)) {
+ if (mAdministratorUids != null && mAdministratorUids.length != 0) {
sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids));
}
@@ -2506,7 +2535,7 @@
@NonNull
public NetworkCapabilities build() {
if (mCaps.getOwnerUid() != Process.INVALID_UID) {
- if (!ArrayUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) {
+ if (!CollectionUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) {
throw new IllegalStateException("The owner UID must be included in "
+ " administrator UIDs.");
}
diff --git a/core/java/android/net/NetworkConfig.java b/packages/Connectivity/framework/src/android/net/NetworkConfig.java
similarity index 100%
rename from core/java/android/net/NetworkConfig.java
rename to packages/Connectivity/framework/src/android/net/NetworkConfig.java
diff --git a/core/java/android/net/NetworkInfo.aidl b/packages/Connectivity/framework/src/android/net/NetworkInfo.aidl
similarity index 100%
rename from core/java/android/net/NetworkInfo.aidl
rename to packages/Connectivity/framework/src/android/net/NetworkInfo.aidl
diff --git a/core/java/android/net/NetworkInfo.java b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
similarity index 100%
rename from core/java/android/net/NetworkInfo.java
rename to packages/Connectivity/framework/src/android/net/NetworkInfo.java
diff --git a/core/java/android/net/NetworkProvider.java b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
similarity index 100%
rename from core/java/android/net/NetworkProvider.java
rename to packages/Connectivity/framework/src/android/net/NetworkProvider.java
diff --git a/core/java/android/net/NetworkRequest.aidl b/packages/Connectivity/framework/src/android/net/NetworkRequest.aidl
similarity index 100%
rename from core/java/android/net/NetworkRequest.aidl
rename to packages/Connectivity/framework/src/android/net/NetworkRequest.aidl
diff --git a/core/java/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
similarity index 100%
rename from core/java/android/net/NetworkRequest.java
rename to packages/Connectivity/framework/src/android/net/NetworkRequest.java
diff --git a/core/java/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
similarity index 100%
rename from core/java/android/net/NetworkUtils.java
rename to packages/Connectivity/framework/src/android/net/NetworkUtils.java
diff --git a/core/java/android/net/PacProxySelector.java b/packages/Connectivity/framework/src/android/net/PacProxySelector.java
similarity index 100%
rename from core/java/android/net/PacProxySelector.java
rename to packages/Connectivity/framework/src/android/net/PacProxySelector.java
diff --git a/core/java/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java
similarity index 93%
rename from core/java/android/net/Proxy.java
rename to packages/Connectivity/framework/src/android/net/Proxy.java
index 03b07e0..03cfbbb 100644
--- a/core/java/android/net/Proxy.java
+++ b/packages/Connectivity/framework/src/android/net/Proxy.java
@@ -16,8 +16,10 @@
package android.net;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
@@ -245,7 +247,19 @@
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final void setHttpProxySystemProperty(ProxyInfo p) {
+ @Deprecated
+ public static void setHttpProxySystemProperty(ProxyInfo p) {
+ setHttpProxyConfiguration(p);
+ }
+
+ /**
+ * Set HTTP proxy configuration for the process to match the provided ProxyInfo.
+ *
+ * If the provided ProxyInfo is null, the proxy configuration will be cleared.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static void setHttpProxyConfiguration(@Nullable ProxyInfo p) {
String host = null;
String port = null;
String exclList = null;
@@ -256,11 +270,11 @@
exclList = ProxyUtils.exclusionListAsString(p.getExclusionList());
pacFileUrl = p.getPacFileUrl();
}
- setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
+ setHttpProxyConfiguration(host, port, exclList, pacFileUrl);
}
/** @hide */
- public static final void setHttpProxySystemProperty(String host, String port, String exclList,
+ public static void setHttpProxyConfiguration(String host, String port, String exclList,
Uri pacFileUrl) {
if (exclList != null) exclList = exclList.replace(",", "|");
if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
diff --git a/core/java/android/net/ProxyInfo.aidl b/packages/Connectivity/framework/src/android/net/ProxyInfo.aidl
similarity index 100%
rename from core/java/android/net/ProxyInfo.aidl
rename to packages/Connectivity/framework/src/android/net/ProxyInfo.aidl
diff --git a/core/java/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
similarity index 100%
rename from core/java/android/net/ProxyInfo.java
rename to packages/Connectivity/framework/src/android/net/ProxyInfo.java
diff --git a/core/java/android/net/RouteInfo.aidl b/packages/Connectivity/framework/src/android/net/RouteInfo.aidl
similarity index 100%
rename from core/java/android/net/RouteInfo.aidl
rename to packages/Connectivity/framework/src/android/net/RouteInfo.aidl
diff --git a/core/java/android/net/RouteInfo.java b/packages/Connectivity/framework/src/android/net/RouteInfo.java
similarity index 100%
rename from core/java/android/net/RouteInfo.java
rename to packages/Connectivity/framework/src/android/net/RouteInfo.java
diff --git a/core/java/android/net/SocketKeepalive.java b/packages/Connectivity/framework/src/android/net/SocketKeepalive.java
similarity index 100%
rename from core/java/android/net/SocketKeepalive.java
rename to packages/Connectivity/framework/src/android/net/SocketKeepalive.java
diff --git a/core/java/android/net/StaticIpConfiguration.aidl b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl
similarity index 100%
rename from core/java/android/net/StaticIpConfiguration.aidl
rename to packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl
diff --git a/core/java/android/net/StaticIpConfiguration.java b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java
similarity index 100%
rename from core/java/android/net/StaticIpConfiguration.java
rename to packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java
diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
similarity index 100%
rename from core/java/android/net/TcpKeepalivePacketData.java
rename to packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
diff --git a/core/java/android/net/TcpRepairWindow.java b/packages/Connectivity/framework/src/android/net/TcpRepairWindow.java
similarity index 100%
rename from core/java/android/net/TcpRepairWindow.java
rename to packages/Connectivity/framework/src/android/net/TcpRepairWindow.java
diff --git a/core/java/android/net/TcpSocketKeepalive.java b/packages/Connectivity/framework/src/android/net/TcpSocketKeepalive.java
similarity index 100%
rename from core/java/android/net/TcpSocketKeepalive.java
rename to packages/Connectivity/framework/src/android/net/TcpSocketKeepalive.java
diff --git a/core/java/android/net/TestNetworkInterface.aidl b/packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl
similarity index 100%
rename from core/java/android/net/TestNetworkInterface.aidl
rename to packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl
diff --git a/core/java/android/net/TestNetworkInterface.java b/packages/Connectivity/framework/src/android/net/TestNetworkInterface.java
similarity index 100%
rename from core/java/android/net/TestNetworkInterface.java
rename to packages/Connectivity/framework/src/android/net/TestNetworkInterface.java
diff --git a/core/java/android/net/TestNetworkManager.java b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
similarity index 100%
rename from core/java/android/net/TestNetworkManager.java
rename to packages/Connectivity/framework/src/android/net/TestNetworkManager.java
diff --git a/core/java/android/net/TransportInfo.java b/packages/Connectivity/framework/src/android/net/TransportInfo.java
similarity index 100%
rename from core/java/android/net/TransportInfo.java
rename to packages/Connectivity/framework/src/android/net/TransportInfo.java
diff --git a/core/java/android/net/UidRange.aidl b/packages/Connectivity/framework/src/android/net/UidRange.aidl
similarity index 100%
rename from core/java/android/net/UidRange.aidl
rename to packages/Connectivity/framework/src/android/net/UidRange.aidl
diff --git a/core/java/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java
similarity index 100%
rename from core/java/android/net/VpnManager.java
rename to packages/Connectivity/framework/src/android/net/VpnManager.java
diff --git a/core/java/android/net/VpnService.java b/packages/Connectivity/framework/src/android/net/VpnService.java
similarity index 100%
rename from core/java/android/net/VpnService.java
rename to packages/Connectivity/framework/src/android/net/VpnService.java
diff --git a/core/java/android/net/apf/ApfCapabilities.aidl b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl
similarity index 100%
rename from core/java/android/net/apf/ApfCapabilities.aidl
rename to packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl
diff --git a/core/java/android/net/apf/ApfCapabilities.java b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
similarity index 100%
rename from core/java/android/net/apf/ApfCapabilities.java
rename to packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
diff --git a/core/java/android/net/util/DnsUtils.java b/packages/Connectivity/framework/src/android/net/util/DnsUtils.java
similarity index 100%
rename from core/java/android/net/util/DnsUtils.java
rename to packages/Connectivity/framework/src/android/net/util/DnsUtils.java
diff --git a/core/java/android/net/util/KeepaliveUtils.java b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
similarity index 100%
rename from core/java/android/net/util/KeepaliveUtils.java
rename to packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
similarity index 100%
rename from core/java/android/net/util/MultinetworkPolicyTracker.java
rename to packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
diff --git a/core/java/android/net/util/SocketUtils.java b/packages/Connectivity/framework/src/android/net/util/SocketUtils.java
similarity index 100%
rename from core/java/android/net/util/SocketUtils.java
rename to packages/Connectivity/framework/src/android/net/util/SocketUtils.java
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index c8f3bd3..8fc3181 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -57,6 +57,7 @@
static_libs: [
"net-utils-device-common",
"net-utils-framework-common",
+ "netd-client",
],
apex_available: [
"//apex_available:platform",
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index 65e75cd..d411831 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -83,9 +83,9 @@
<string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak tableta honetan."</string>
<string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telebista honetan."</string>
<string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telefono honetan."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_off.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_off.xml
index 8a73fb5..2be00b9 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_off.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_off.xml
@@ -15,11 +15,17 @@
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="4dp"
+ android:left="4dp"
+ android:right="4dp"
+ android:bottom="4dp">
- <size android:height="24dp" android:width="24dp" />
- <solid android:color="@color/thumb_off" />
- <stroke android:width="4dp" android:color="@android:color/transparent" />
+ <shape android:shape="oval" >
+ <size android:height="20dp" android:width="20dp" />
+ <solid android:color="@color/thumb_off" />
+ </shape>
-</shape>
+ </item>
+</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_on.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_on.xml
index 8a0af00..e85eb42 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_on.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_on.xml
@@ -15,11 +15,17 @@
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="4dp"
+ android:left="4dp"
+ android:right="4dp"
+ android:bottom="4dp">
- <size android:height="24dp" android:width="24dp" />
- <solid android:color="?android:attr/colorAccent" />
- <stroke android:width="4dp" android:color="@android:color/transparent" />
+ <shape android:shape="oval" >
+ <size android:height="20dp" android:width="20dp" />
+ <solid android:color="?android:attr/colorAccent" />
+ </shape>
-</shape>
+ </item>
+</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off.xml
index 1be3a8e..b29f459 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off.xml
@@ -20,7 +20,7 @@
<item
android:width="13.33dp"
android:height="1.67dp"
- android:left="19dp"
+ android:left="21dp"
android:gravity="center"
android:drawable="@drawable/track_off_indicator" />
</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off_background.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off_background.xml
index 3cc490f..c838654 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off_background.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off_background.xml
@@ -17,17 +17,17 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="24dp"
- android:viewportWidth="48"
- android:viewportHeight="24">
+ android:width="52dp"
+ android:height="28dp"
+ android:viewportWidth="52"
+ android:viewportHeight="28">
<group>
<clip-path
- android:pathData="M12 0H36C42.6274 0 48 5.37258 48 12C48 18.6274 42.6274 24 36 24H12C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0Z" />
+ android:pathData="M14 0H38C45.732 0 52 6.26801 52 14C52 21.732 45.732 28 38 28H14C6.26801 28 0 21.732 0 14C0 6.26801 6.26801 0 14 0Z" />
<path
- android:pathData="M0 0V24H48V0"
+ android:pathData="M0 0V28H52V0"
android:fillColor="@color/track_off" />
</group>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on.xml
index 2553891..cf24112 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on.xml
@@ -21,6 +21,6 @@
android:width="13.19dp"
android:height="10.06dp"
android:gravity="center"
- android:right="19dp"
+ android:right="21dp"
android:drawable="@drawable/track_on_indicator" />
</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on_background.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on_background.xml
index 68ce19b..bb1a7ef 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on_background.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on_background.xml
@@ -17,19 +17,20 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="24dp"
- android:viewportWidth="48"
- android:viewportHeight="24"
+ android:width="52dp"
+ android:height="28dp"
+ android:viewportWidth="52"
+ android:viewportHeight="28"
android:tint="@*android:color/switch_track_material">
<group>
<clip-path
- android:pathData="M12 0H36C42.6274 0 48 5.37258 48 12C48 18.6274 42.6274 24 36 24H12C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0Z" />
+ android:pathData="M14 0H38C45.732 0 52 6.26801 52 14C52 21.732 45.732 28 38 28H14C6.26801 28 0 21.732 0 14C0 6.26801 6.26801 0 14 0Z" />
<path
- android:pathData="M0 0V24H48V0"
+ android:pathData="M0 0V28H52V0"
android:fillColor="@*android:color/white_disabled_material" />
+
</group>
</vector>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
index 7e3ce9d..52779bc 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
@@ -26,7 +26,7 @@
android:minHeight="@dimen/min_switch_bar_height"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:background="?android:attr/colorBackground"
+ android:background="?android:attr/selectableItemBackground"
android:paddingLeft="@dimen/switchbar_margin_start"
android:paddingRight="@dimen/switchbar_margin_end">
@@ -35,7 +35,7 @@
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
- android:paddingRight="54dp"
+ android:layout_marginRight="16dp"
android:layout_gravity="center_vertical"
android:maxLines="2"
android:ellipsize="end"
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml
index 9dc0af3..147db77 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml
@@ -17,7 +17,6 @@
<resources>
- <color name="title_text_color">@*android:color/primary_text_dark</color>
<color name="thumb_off">#BFFFFFFF</color>
<color name="track_off">@*android:color/material_grey_600</color>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
index 8194bdd..147db77 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
@@ -17,7 +17,6 @@
<resources>
- <color name="title_text_color">@*android:color/primary_text_light</color>
<color name="thumb_off">#BFFFFFFF</color>
<color name="track_off">@*android:color/material_grey_600</color>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index dd443de..b145c9b 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -18,13 +18,13 @@
<resources>
<!-- Size of layout margin left -->
- <dimen name="switchbar_margin_start">26dp</dimen>
+ <dimen name="switchbar_margin_start">22dp</dimen>
<!-- Size of layout margin right -->
- <dimen name="switchbar_margin_end">24dp</dimen>
+ <dimen name="switchbar_margin_end">16dp</dimen>
<!-- Minimum width of switch -->
- <dimen name="min_switch_width">48dp</dimen>
+ <dimen name="min_switch_width">52dp</dimen>
<!-- Minimum width of switch bar -->
<dimen name="min_switch_bar_height">72dp</dimen>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
index fbb896c..59b5899 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
@@ -19,7 +19,6 @@
<style name="MainSwitchText">
<item name="android:textSize">20sp</item>
- <item name="android:textColor">@color/title_text_color</item>
</style>
<style name="Settings.MainSwitch" parent="@android:style/Widget.Material.CompoundButton.Switch">
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 532c996..74b6578 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -136,10 +136,8 @@
* Show the MainSwitchBar
*/
public void show() {
- if (!isShowing()) {
- setVisibility(View.VISIBLE);
- mSwitch.setOnCheckedChangeListener(this);
- }
+ setVisibility(View.VISIBLE);
+ mSwitch.setOnCheckedChangeListener(this);
}
/**
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 dae0e70..274bf8d 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -17,9 +17,11 @@
package com.android.settingslib.widget;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.TextUtils;
import android.util.AttributeSet;
-import android.widget.Switch;
+import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.TwoStatePreference;
@@ -38,30 +40,29 @@
private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>();
private MainSwitchBar mMainSwitchBar;
- private Switch mSwitch;
private String mTitle;
private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin;
public MainSwitchPreference(Context context) {
super(context);
- init();
+ init(context, null);
}
public MainSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- init();
+ init(context, attrs);
}
public MainSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- init();
+ init(context, attrs);
}
public MainSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- init();
+ init(context, attrs);
}
@Override
@@ -76,8 +77,21 @@
registerListenerToSwitchBar();
}
- private void init() {
+ private void init(Context context, AttributeSet attrs) {
setLayoutResource(R.layout.main_switch_layout);
+
+ if (attrs != null) {
+ 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());
+ }
+ a.recycle();
+ }
}
/**
diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
index f346a59..3331550 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/Android.bp
+++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
@@ -6,6 +6,7 @@
static_libs: [
"androidx.preference_preference",
+ "androidx-constraintlayout_constraintlayout",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
index 9dbd5fa..f45105d 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
+++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
@@ -17,6 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
@@ -26,29 +27,40 @@
android:paddingTop="32dp"
android:paddingBottom="32dp">
- <RelativeLayout
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/usage_summary"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_alignBaseline="@id/total_summary"
- android:singleLine="true"
+ app:layout_constraintWidth_percent="0.45"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBaseline_toBaselineOf="@id/total_summary"
android:ellipsize="marquee"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Display1"
- android:textSize="20sp"/>
+ android:textSize="14sp"
+ android:textAlignment="viewStart"/>
<TextView
android:id="@+id/total_summary"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:singleLine="true"
+ app:layout_constraintWidth_percent="0.45"
+ app:layout_constraintEnd_toStartOf="@id/custom_content"
android:ellipsize="marquee"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"/>
- </RelativeLayout>
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
+ android:textSize="14sp"
+ android:textAlignment="viewEnd"/>
+ <FrameLayout
+ android:id="@+id/custom_content"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="@id/total_summary"
+ app:layout_constraintWidth_percent="0.1"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
<ProgressBar
android:id="@android:id/progress"
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
index 950a8b4..af64a1d 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -22,6 +22,9 @@
import android.text.TextUtils;
import android.text.style.RelativeSizeSpan;
import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -41,6 +44,7 @@
private CharSequence mUsageSummary;
private CharSequence mTotalSummary;
+ private ImageView mCustomImageView;
private int mPercent = -1;
/**
@@ -110,6 +114,15 @@
notifyChanged();
}
+ /** Set custom ImageView to the right side of total summary. */
+ public <T extends ImageView> void setCustomContent(T imageView) {
+ if (imageView == mCustomImageView) {
+ return;
+ }
+ mCustomImageView = imageView;
+ notifyChanged();
+ }
+
/**
* Binds the created View to the data for this preference.
*
@@ -141,6 +154,15 @@
progressBar.setIndeterminate(false);
progressBar.setProgress(mPercent);
}
+
+ final FrameLayout customLayout = (FrameLayout) holder.findViewById(R.id.custom_content);
+ if (mCustomImageView == null) {
+ customLayout.removeAllViews();
+ customLayout.setVisibility(View.GONE);
+ } else {
+ customLayout.addView(mCustomImageView);
+ customLayout.setVisibility(View.VISIBLE);
+ }
}
private CharSequence enlargeFontOfNumber(CharSequence summary) {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 5563003..7556ace 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1484,5 +1484,5 @@
<!-- Content description of the Ethernet connection when disconnected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_ethernet_disconnected">Ethernet disconnected.</string>
<!-- Content description of the Ethernet connection when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_ethernet_connected">Ethernet connected.</string>
+ <string name="accessibility_ethernet_connected">Ethernet.</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index f83c7a2..6cf53d0b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -479,7 +479,7 @@
}
}
- class RouterManagerCallback extends MediaRouter2Manager.Callback {
+ class RouterManagerCallback implements MediaRouter2Manager.Callback {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 5580afe..78282fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -19,6 +19,7 @@
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
@@ -38,7 +39,9 @@
import com.android.settingslib.R;
import com.android.settingslib.Utils;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Track status of Wi-Fi for the Sys UI.
@@ -50,6 +53,7 @@
private final NetworkScoreManager mNetworkScoreManager;
private final ConnectivityManager mConnectivityManager;
private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Set<Integer> mNetworks = new HashSet<>();
private final WifiNetworkScoreCache.CacheListener mCacheListener =
new WifiNetworkScoreCache.CacheListener(mHandler) {
@Override
@@ -64,6 +68,20 @@
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onAvailable(
+ Network network, NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties, boolean blocked) {
+ boolean isVcnOverWifi =
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && (Utils.tryGetWifiInfoForVcn(networkCapabilities) != null);
+ boolean isWifi =
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+ if (isVcnOverWifi || isWifi) {
+ mNetworks.add(network.getNetId());
+ }
+ }
+
// Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable
// and onLinkPropertiesChanged.
@Override
@@ -84,9 +102,12 @@
@Override
public void onLost(Network network) {
- updateWifiInfo(null);
- updateStatusLabel();
- mCallback.run();
+ if (mNetworks.contains(network.getNetId())) {
+ mNetworks.remove(network.getNetId());
+ updateWifiInfo(null);
+ updateStatusLabel();
+ mCallback.run();
+ }
}
};
private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
index 85e2174..1a8477d 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
@@ -18,11 +18,15 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
import android.content.Context;
import android.text.SpannedString;
import android.text.style.RelativeSizeSpan;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -97,4 +101,30 @@
.findViewById(android.R.id.progress);
assertThat(progressBar.getProgress()).isEqualTo((int) (31.0f / 80 * 100));
}
+
+ @Test
+ public void setCustomContent_setNullImageView_noChild() {
+ mUsageProgressBarPreference.setCustomContent(null /* imageView */);
+
+ mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+ final FrameLayout customContent =
+ (FrameLayout) mViewHolder.findViewById(R.id.custom_content);
+ assertThat(customContent.getChildCount()).isEqualTo(0);
+ assertThat(customContent.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setCustomContent_setImageView_oneChild() {
+ final ImageView imageView = mock(ImageView.class);
+ mUsageProgressBarPreference.setCustomContent(imageView);
+
+ mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+ final FrameLayout customContent =
+ (FrameLayout) mViewHolder.findViewById(R.id.custom_content);
+ assertThat(customContent.getChildCount()).isEqualTo(1);
+ assertThat(customContent.getChildAt(0)).isEqualTo(imageView);
+ assertThat(customContent.getVisibility()).isEqualTo(View.VISIBLE);
+ }
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 60620bd..e92591d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -38,6 +38,7 @@
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
Settings.Secure.ADAPTIVE_SLEEP,
+ Settings.Secure.CAMERA_AUTOROTATE,
Settings.Secure.AUTOFILL_SERVICE,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 66165b6..ad6a531 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -147,5 +147,10 @@
VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.ONE_HANDED_KEYGUARD_SIDE,
+ new InclusiveIntegerRangeValidator(
+ /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT,
+ /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 3f46262..ed2b6c9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -57,6 +57,7 @@
VALIDATORS.put(
Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.CAMERA_AUTOROTATE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.AUTOFILL_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR);
VALIDATORS.put(
Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 266bfe0..6568bff 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -846,8 +846,8 @@
try {
stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
- loadBooleanSetting(stmt, Settings.System.USER_ROTATION,
- R.integer.def_user_rotation); // should be zero degrees
+ loadIntegerSetting(stmt, Settings.System.USER_ROTATION,
+ R.integer.def_user_rotation);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -2265,6 +2265,8 @@
loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION,
R.bool.def_accelerometer_rotation);
+ loadIntegerSetting(stmt, Settings.System.USER_ROTATION, R.integer.def_user_rotation);
+
loadDefaultHapticSettings(stmt);
loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 83ded43..1ce738e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1969,6 +1969,12 @@
Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
SecureSettingsProto.BLUETOOTH_ON_WHILE_DRIVING);
+ final long smartAutoRotateToken = p.start(SecureSettingsProto.CAMERA_AUTOROTATE);
+ dumpSetting(s, p,
+ Settings.Secure.CAMERA_AUTOROTATE,
+ SecureSettingsProto.CameraAutorotate.ENABLED);
+ p.end(smartAutoRotateToken);
+
final long cameraToken = p.start(SecureSettingsProto.CAMERA);
dumpSetting(s, p,
Settings.Secure.CAMERA_GESTURE_DISABLED,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index efd941e..c11877a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -272,6 +272,7 @@
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
Settings.Global.ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT,
+ Settings.Global.ENABLE_MULTI_SLOT_TIMEOUT_MILLIS,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
@@ -280,7 +281,9 @@
Settings.Global.EUICC_UNSUPPORTED_COUNTRIES,
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
+ Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7eab559..1af7781 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -304,9 +304,6 @@
<uses-permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" />
<!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
- <uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" />
-
- <!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
<uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL" />
<!-- Permission needed to use wifi usability API's for CtsNetTestCases -->
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ca9dcd6..5af0244 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -116,7 +116,6 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MONITOR_INPUT" />
<uses-permission android:name="android.permission.INPUT_CONSUMER" />
- <uses-permission android:name="android.permission.USE_BACKGROUND_BLUR" />
<!-- DreamManager -->
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
diff --git a/packages/SystemUI/docs/sos_gesture.md b/packages/SystemUI/docs/sos_gesture.md
new file mode 100644
index 0000000..1a9144b
--- /dev/null
+++ b/packages/SystemUI/docs/sos_gesture.md
@@ -0,0 +1,26 @@
+# How 5-tapping power launches Emergency Sos
+
+_as of Jan 2021_
+
+Note that the flow is a simplified version of the camera launch flow.
+
+
+### Sequence of events
+
+
+1. [PhoneWindowManager.java](/services/core/java/com/android/server/policy/PhoneWindowManager.java) is responsible for all power button presses (see `interceptPowerKeyDown`).
+2. Even though PWMgr has a lot of logic to detect all manner of power button multipresses and gestures, it also checks with GestureLauncherService, which is also [offered the chance](/services/core/java/com/android/server/policy/PhoneWindowManager.java#943) to [intercept](/services/core/java/com/android/server/GestureLauncherService.java#358) the power key.
+3. GLS is responsible for the emergoncy sos timeout, and if it detects one, it [forwards it to the StatusBarManagerService](/services/core/java/com/android/server/GestureLauncherService.java#475) (which hands it off to SystemUI).
+4. Inside SystemUI, [onEmergencyActionLaunchGestureDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#4039) and determines
+ 1. If the gesture is enabled (else do nothing)
+ 2. If there is an app to handle the gesture (else do nothing)
+ 2. whether the screen is on; if not, we need to delay until that happens
+5. Assuming there is an app, and the setting is one launch Emergengy Flow immediately. [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#4077)
+ 1. Note that we cannot have an intent resolver, so we launch the default.
+
+**Which intent launches?**
+
+Due to the nature of the gesture, we need the flow to work behind the lockscreen, and without disambiguation.
+Thus, we always launch the same intent, and verify that there is only one matching intent-filter in the system image.
+
+[The emergengy sos intent action](packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java#36).
diff --git a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
index 99c70a5..b96c07e 100644
--- a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
+++ b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
@@ -17,9 +17,8 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/wallpaperTextColorSecondary">
<item android:id="@android:id/background">
- <shape
- android:color="@android:color/transparent">
- <stroke android:width="1dp" android:color="?android:attr/textColorSecondary"/>
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorAccent"/>
<corners android:radius="24dp"/>
</shape>
</item>
@@ -29,4 +28,4 @@
<corners android:radius="24dp"/>
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
index 7986809..b5f55af 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -24,7 +24,7 @@
<include
style="@style/BouncerSecurityContainer"
layout="@layout/keyguard_host_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 80c8a28..35a2195 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -76,7 +76,7 @@
android:textSize="100dp"
android:includeFontPadding="false"
android:fontFamily="@font/clock"
- android:lineSpacingMultiplier=".65"
+ android:lineSpacingMultiplier=".7"
android:typeface="monospace"
android:elegantTextHeight="false"
dozeWeight="200"
@@ -97,7 +97,7 @@
android:gravity="center_horizontal"
android:textSize="@dimen/large_clock_text_size"
android:includeFontPadding="false"
- android:lineSpacingMultiplier=".65"
+ android:lineSpacingMultiplier=".7"
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
index 370576b..0ee1b69 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
@@ -43,7 +43,7 @@
<com.android.keyguard.EmergencyButton
android:id="@+id/emergency_call_button"
android:layout_width="wrap_content"
- android:layout_height="32dp"
+ android:layout_height="48dp"
android:layout_marginBottom="12dp"
android:text="@*android:string/lockscreen_emergency_call"
style="@style/Keyguard.TextView.EmergencyButton" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index 796123db..c75ee51 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -32,9 +32,8 @@
<com.android.keyguard.KeyguardSecurityContainer
android:id="@+id/keyguard_security_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- androidprv:layout_maxHeight="@dimen/keyguard_security_max_height"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:padding="0dp"
@@ -42,13 +41,14 @@
android:layout_gravity="center">
<com.android.keyguard.KeyguardSecurityViewFlipper
android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="@dimen/keyguard_security_view_top_margin"
android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
+ android:layout_gravity="center"
android:gravity="center">
</com.android.keyguard.KeyguardSecurityViewFlipper>
</com.android.keyguard.KeyguardSecurityContainer>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index dc2d11d..1a38585 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -67,6 +67,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal" />
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index aa14645a..6ae759c 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -188,6 +188,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardPINView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index 64ccefd..f709424 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -198,6 +198,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardSimPinView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index dc77bd3..2f9fed6 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -46,7 +46,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
- android:layout_weight="1"
android:layoutDirection="ltr"
>
<include layout="@layout/keyguard_esim_area"
@@ -200,5 +199,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardSimPukView>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
new file mode 100644
index 0000000..e09bf7e
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
@@ -0,0 +1,20 @@
+<?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>
+ <bool name="can_use_one_handed_bouncer">true</bool>
+</resources>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 8d9d6ee..6176f7c 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,4 +22,5 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
+ <bool name="can_use_one_handed_bouncer">false</bool>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index aa87107..a928b75 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -40,6 +40,8 @@
<dimen name="keyguard_security_view_top_margin">8dp</dimen>
<dimen name="keyguard_security_view_lateral_margin">36dp</dimen>
+ <dimen name="keyguard_eca_top_margin">24dp</dimen>
+
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
<dimen name="eca_overlap">-10dip</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 2e99dea..cd82b80 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -23,12 +23,13 @@
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
</style>
<style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textSize">14dp</item>
<item name="android:background">@drawable/kg_emergency_button_background</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:paddingLeft">12dp</item>
<item name="android:paddingRight">12dp</item>
+ <item name="android:stateListAnimator">@null</item>
</style>
<style name="NumPadKey" parent="Theme.SystemUI">
<item name="android:colorControlNormal">?android:attr/colorBackground</item>
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml b/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml
index cf64136..5cb6f46 100644
--- a/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml
@@ -21,16 +21,16 @@
>
<shape android:shape="oval">
<size
- android:height="28dp"
- android:width="28dp"
+ android:height="@dimen/ongoing_appops_dialog_circle_size"
+ android:width="@dimen/ongoing_appops_dialog_circle_size"
/>
<solid android:color="@color/privacy_circle_camera" />
</shape>
</item>
<item android:id="@id/icon"
android:gravity="center"
- android:width="20dp"
- android:height="20dp"
+ android:width="@dimen/ongoing_appops_dialog_icon_size"
+ android:height="@dimen/ongoing_appops_dialog_icon_size"
android:drawable="@*android:drawable/perm_group_camera"
/>
</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_location.xml b/packages/SystemUI/res/drawable/privacy_item_circle_location.xml
index 0a6a4a3..bff9b4b 100644
--- a/packages/SystemUI/res/drawable/privacy_item_circle_location.xml
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_location.xml
@@ -21,16 +21,16 @@
>
<shape android:shape="oval">
<size
- android:height="28dp"
- android:width="28dp"
+ android:height="@dimen/ongoing_appops_dialog_circle_size"
+ android:width="@dimen/ongoing_appops_dialog_circle_size"
/>
<solid android:color="@color/privacy_circle_microphone_location" />
</shape>
</item>
<item android:id="@id/icon"
android:gravity="center"
- android:width="20dp"
- android:height="20dp"
- android:drawable="@*android:drawable/perm_group_microphone"
+ android:width="@dimen/ongoing_appops_dialog_icon_size"
+ android:height="@dimen/ongoing_appops_dialog_icon_size"
+ android:drawable="@*android:drawable/perm_group_location"
/>
</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml b/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml
index 0a6a4a3..28466c8 100644
--- a/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml
@@ -21,16 +21,16 @@
>
<shape android:shape="oval">
<size
- android:height="28dp"
- android:width="28dp"
+ android:height="@dimen/ongoing_appops_dialog_circle_size"
+ android:width="@dimen/ongoing_appops_dialog_circle_size"
/>
<solid android:color="@color/privacy_circle_microphone_location" />
</shape>
</item>
<item android:id="@id/icon"
android:gravity="center"
- android:width="20dp"
- android:height="20dp"
+ android:width="@dimen/ongoing_appops_dialog_icon_size"
+ android:height="@dimen/ongoing_appops_dialog_icon_size"
android:drawable="@*android:drawable/perm_group_microphone"
/>
</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
index 59dad0e..b8ea622 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
@@ -17,6 +17,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid
- android:color="?android:attr/textColorPrimary" />
+ android:color="?android:attr/textColorSecondary" />
<corners android:radius="2dp" />
</shape>
diff --git a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
index 71b414f..93664da 100644
--- a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
@@ -25,7 +25,7 @@
android:layout_marginBottom="@dimen/screenshot_offset_y"
android:scaleType="fitStart"
android:elevation="@dimen/screenshot_preview_elevation"
- android:visibility="gone"
+ android:visibility="invisible"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
android:contentDescription="@string/screenshot_edit_label"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index fc68a64..8f3345f 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -22,63 +22,88 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ImageView
- android:id="@+id/preview"
+ <Button
+ android:id="@+id/save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginVertical="8dp"
- android:layout_marginHorizontal="48dp"
- android:adjustViewBounds="true"
- app:layout_constrainedHeight="true"
- app:layout_constrainedWidth="true"
- app:layout_constraintBottom_toBottomOf="@id/guideline"
- app:layout_constraintEnd_toEndOf="parent"
+ android:text="@string/save"
+ app:layout_constraintEnd_toStartOf="@id/cancel"
+ app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- tools:background="?android:colorBackground"
- tools:minHeight="100dp"
- tools:minWidth="100dp" />
+ app:layout_constraintBottom_toTopOf="@id/guideline" />
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/cancel"
+ app:layout_constraintEnd_toStartOf="@id/edit"
+ app:layout_constraintStart_toEndOf="@id/save"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/guideline" />
+
+ <Button
+ android:id="@+id/edit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/screenshot_edit_label"
+ app:layout_constraintEnd_toStartOf="@id/share"
+ app:layout_constraintStart_toEndOf="@id/cancel"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/guideline" />
+
+ <Button
+ android:id="@+id/share"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@*android:string/share"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/edit"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/guideline" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
- app:layout_constraintGuide_percent="0.9" />
+ app:layout_constraintGuide_percent="0.1" />
- <Button
- android:id="@+id/close"
+ <ImageView
+ android:id="@+id/preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_margin="8dp"
- android:text="Close"
- app:layout_constraintEnd_toStartOf="@+id/edit"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/guideline" />
-
- <Button
- android:id="@+id/edit"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="8dp"
- android:text="Edit"
- app:layout_constraintEnd_toStartOf="@+id/share"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toEndOf="@+id/close"
- app:layout_constraintTop_toTopOf="@+id/guideline" />
-
- <Button
- android:id="@+id/share"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="8dp"
- android:text="Share"
+ android:layout_marginBottom="24dp"
+ android:layout_marginHorizontal="48dp"
+ android:adjustViewBounds="true"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintTop_toBottomOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toEndOf="@+id/edit"
- app:layout_constraintTop_toTopOf="@+id/guideline" />
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ tools:background="?android:colorBackground"
+ tools:minHeight="100dp"
+ tools:minWidth="100dp" />
+
+ <com.android.systemui.screenshot.CropView
+ android:id="@+id/crop_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="24dp"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintTop_toBottomOf="@id/guideline"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:handleThickness="3dp"
+ app:handleColor="@*android:color/accent_device_default"
+ app:scrimColor="#9444"
+ tools:background="?android:colorBackground"
+ tools:minHeight="100dp"
+ tools:minWidth="100dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/privacy_dialog.xml b/packages/SystemUI/res/layout/privacy_dialog.xml
index 5db247e..4d77a0d 100644
--- a/packages/SystemUI/res/layout/privacy_dialog.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog.xml
@@ -22,7 +22,13 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/ongoing_appops_dialog_side_margins"
android:layout_marginEnd="@dimen/ongoing_appops_dialog_side_margins"
+ android:layout_marginTop="8dp"
android:orientation="vertical"
- android:padding = "8dp"
+ android:paddingLeft="@dimen/ongoing_appops_dialog_side_padding"
+ android:paddingRight="@dimen/ongoing_appops_dialog_side_padding"
+ android:paddingBottom="12dp"
+ android:paddingTop="8dp"
android:background="@drawable/privacy_dialog_bg"
-/>
\ No newline at end of file
+/>
+<!-- 12dp padding bottom so there's 20dp total under the icon -->
+<!-- 8dp padding top, as there's 4dp margin in each row -->
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/privacy_dialog_item.xml b/packages/SystemUI/res/layout/privacy_dialog_item.xml
index 882e968..91ffe22 100644
--- a/packages/SystemUI/res/layout/privacy_dialog_item.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog_item.xml
@@ -16,19 +16,22 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/privacy_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:orientation="horizontal"
- android:layout_marginTop="8dp"
+ android:layout_marginTop="4dp"
+ android:importantForAccessibility="yes"
+ android:focusable="true"
>
+ <!-- 4dp marginTop makes 20dp minimum between icons -->
<ImageView
android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="@dimen/ongoing_appops_dialog_circle_size"
+ android:layout_height="@dimen/ongoing_appops_dialog_circle_size"
android:layout_gravity="center_vertical"
- android:layout_marginStart="12dp"
- android:layout_marginEnd="12dp"
+ android:importantForAccessibility="no"
/>
<TextView
@@ -38,25 +41,21 @@
android:layout_width="0dp"
android:layout_weight="1"
android:layout_gravity="center_vertical"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
android:gravity="start | center_vertical"
- android:textAppearance="@style/TextAppearance.QS.TileLabel"
+ android:textAppearance="@style/TextAppearance.PrivacyDialog"
+ android:lineHeight="20sp"
/>
- <FrameLayout
- android:id="@+id/link"
- android:layout_width="48dp"
- android:layout_height="48dp"
+ <ImageView
+ android:layout_height="24dp"
+ android:layout_width="24dp"
android:layout_gravity="center_vertical"
- android:background="?android:attr/selectableItemBackground"
- >
-
- <ImageView
- android:layout_height="24dp"
- android:layout_width="24dp"
- android:layout_gravity="center"
- android:src="@*android:drawable/ic_chevron_end"
- android:tint="?android:attr/textColorPrimary"
- />
- </FrameLayout>
+ android:src="@*android:drawable/ic_chevron_end"
+ android:tint="?android:attr/textColorPrimary"
+ />
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 75f76b4..d6385ff 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -50,18 +50,22 @@
android:layout="@layout/qs_panel"
android:layout_width="@dimen/qs_panel_width"
android:layout_height="match_parent"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
android:clipToPadding="false"
android:clipChildren="false"
- systemui:viewType="com.android.systemui.plugins.qs.QS" />
+ systemui:viewType="com.android.systemui.plugins.qs.QS"
+ systemui:layout_constraintStart_toStartOf="parent"
+ systemui:layout_constraintEnd_toEndOf="parent"
+ />
<com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
android:layout_marginTop="@dimen/notification_panel_margin_top"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="match_parent"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:layout_marginBottom="@dimen/close_handle_underlap" />
+ android:layout_marginBottom="@dimen/close_handle_underlap"
+ systemui:layout_constraintStart_toStartOf="parent"
+ systemui:layout_constraintEnd_toEndOf="parent"
+ />
<include layout="@layout/ambient_indication"
android:id="@+id/ambient_indication_container" />
diff --git a/packages/SystemUI/res/layout/udfps_animation_view.xml b/packages/SystemUI/res/layout/udfps_animation_view.xml
new file mode 100644
index 0000000..380dd85
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_animation_view.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.biometrics.UdfpsAnimationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/layout/udfps_surface_view.xml b/packages/SystemUI/res/layout/udfps_surface_view.xml
new file mode 100644
index 0000000..18858d6
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_surface_view.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.biometrics.UdfpsSurfaceView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 6dcb502..5db7c25 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Skermopname"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Begin"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Om voort te gaan, moet <b><xliff:g id="APP">%s</xliff:g></b> toegang tot jou toestel se mikrofoon hê."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Om voort te gaan, moet <b><xliff:g id="APP">%s</xliff:g></b> toegang tot jou toestel se kamera hê."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Toestel"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swiep op om programme te wissel"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Sleep regs om programme vinnig te wissel"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tik weer om oop te maak"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swiep op om oop te maak"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Swiep op om weer te probeer"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ontsluit om NFC te gebruik"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Hierdie toestel behoort aan jou organisasie"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Hierdie toestel behoort aan <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Swiep vanaf ikoon vir foon"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programme gebruik tans jou <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" en "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> gebruik tans die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> het onlangs die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> gebruik"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(onderneming)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Oproep"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(deur <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ligging"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofoon"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index d1954a6..1af18fe 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"መተግበሪያዎች የእርስዎን <xliff:g id="TYPES_LIST">%s</xliff:g> እየተጠቀሙ ነው።"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"፣ "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" እና "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ን እየተጠቀመ ነው"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> በቅርብ ጊዜ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ን ይጠቀማል።"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ድርጅት)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"የስልክ ጥሪ"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(እስከ <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ካሜራ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"አካባቢ"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"ማይክሮፎን"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index f2652d3..b34b39a 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -424,10 +424,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"تسجيل الشاشة"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"بدء"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"إيقاف"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"للمتابعة، يحتاج تطبيق <b><xliff:g id="APP">%s</xliff:g></b> إلى الوصول إلى ميكروفون الجهاز."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"للمتابعة، يحتاج تطبيق <b><xliff:g id="APP">%s</xliff:g></b> إلى الوصول إلى كاميرا الجهاز."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"الجهاز"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"مرّر سريعًا لأعلى لتبديل التطبيقات"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"اسحب لليسار للتبديل السريع بين التطبيقات"</string>
@@ -450,8 +448,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"انقر مرة أخرى للفتح"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"يمكنك الفتح بالتمرير سريعًا لأعلى."</string>
<string name="keyguard_retry" msgid="886802522584053523">"مرِّر سريعًا للأعلى لإعادة المحاولة."</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"يجب فتح قفل الشاشة لاستخدام تقنية الاتصال قصير المدى (NFC)."</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"هذا الجهاز يخص مؤسستك."</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
<string name="phone_hint" msgid="6682125338461375925">"يمكنك التمرير سريعًا من الرمز لتشغيل الهاتف"</string>
@@ -990,6 +987,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"تستخدم التطبيقات <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" و "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"يستخدم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> عملية <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> الآن."</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"استخدَم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> عملية <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> مؤخرًا."</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(للمؤسسات)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"مكالمة هاتفية"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(من خلال <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"الكاميرا"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"الموقع"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"الميكروفون"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 8abb5ed..88b2bb2 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"এপ্লিকেশ্বনসমূহে আপোনাৰ <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যৱহাৰ কৰি আছে।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" আৰু "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>এ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যৱহাৰ কৰি আছে"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>এ শেহতীয়াকৈ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যৱহাৰ কৰিছে"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(এণ্টাৰপ্ৰাইজ)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>ৰ জৰিয়তে)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"কেমেৰা"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"অৱস্থান"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"মাইক্ৰ\'ফ\'ন"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 352282c..6f1c347 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekran yazması"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Başlayın"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dayandırın"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Davam etmək üçün <b><xliff:g id="APP">%s</xliff:g></b> tətbiqi cihazın mikrofonuna giriş tələb edir."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Davam etmək üçün <b><xliff:g id="APP">%s</xliff:g></b> tətbiqi cihazın kamerasına giriş tələb edir."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Cihaz"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Tətbiqi dəyişmək üçün yuxarı sürüşdürün"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Tətbiqləri cəld dəyişmək üçün sağa çəkin"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Açmaq üçün yenidən tıklayın"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Açmaq üçün yuxarı sürüşdürün"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Yenidən cəhd etmək üçün yuxarı sürüşdürün"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC istifadə etmək üçün kiliddən çıxarın"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Bu cihaz təşkilatınıza məxsusdur"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> təşkilatına məxsusdur"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefon üçün ikonadan sürüşdürün"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Tətbiqlər <xliff:g id="TYPES_LIST">%s</xliff:g> istifadə edir."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" və "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> istifadə edir"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> bu yaxınlarda <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> istifadə edib"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(korporativ)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefon zəngi"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> vasitəsilə)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"məkan"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index ba131c4..2185a66 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -418,10 +418,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Snimak ekrana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Počnite"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"<b><xliff:g id="APP">%s</xliff:g></b> zahteva pristup mikrofonu uređaja radi nastavljanja."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"<b><xliff:g id="APP">%s</xliff:g></b> zahteva pristup kameri uređaja radi nastavljanja."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Uređaj"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Prevucite nagore da biste menjali aplikacije"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Prevucite udesno da biste brzo promenili aplikacije"</string>
@@ -444,8 +442,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Dodirnite ponovo da biste otvorili"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Prevucite nagore da biste otvorili"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Prevucite nagore da biste probali ponovo"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Otključajte da biste koristili NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada organizaciji"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Prevucite od ikone za telefon"</string>
@@ -975,6 +972,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> koristi: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> je nedavno koristila: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(za preduzeća)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski poziv"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(preko: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kameru"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokaciju"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 61f7188..e5356ae 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -420,10 +420,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Запіс экрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Пачаць"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Спыніць"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Каб працягнуць, дайце праграме <b><xliff:g id="APP">%s</xliff:g></b> доступ да мікрафона прылады."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Каб працягнуць, дайце праграме <b><xliff:g id="APP">%s</xliff:g></b> доступ да камеры прылады."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Прылада"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Правядзіце ўверх, каб пераключыць праграмы"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Каб хутка пераключыцца паміж праграмамі, перацягніце ўправа"</string>
@@ -446,8 +444,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Дакраніцеся яшчэ раз, каб адкрыць"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Каб адкрыць, прагарніце ўверх"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Прагартайце ўверх, каб паўтарыць спробу"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Разблакіруйце, каб выкарыстоўваць NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Гэта прылада належыць вашай арганізацыі"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Гэта прылада належыць арганізацыі \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string>
<string name="phone_hint" msgid="6682125338461375925">"Тэлефон: правядзіце пальцам ад значка"</string>
@@ -980,6 +977,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Праграмы выкарыстоўваюць: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" і "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Праграма \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" выкарыстоўвае праграму \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Праграма \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" нядаўна выкарыстоўвала праграму \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(прадпрыемства)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Тэлефонны выклік"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(праз праграму \"<xliff:g id="ATTRIBUTION">%s</xliff:g>\")"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"геалакацыя"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"мікрафон"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index d6ba258..adf852b 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Записване на екрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Старт"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Стоп"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"За да продължите, <b><xliff:g id="APP">%s</xliff:g></b> се нуждае от достъп до микрофона на устройството ви."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"За да продължите, <b><xliff:g id="APP">%s</xliff:g></b> се нуждае от достъп до камерата на устройството ви."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Устройство"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Прекарайте пръст нагоре, за да превключите между приложенията"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Плъзнете надясно за бързо превключване между приложенията"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Докоснете отново, за да отворите"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Прекарайте пръст нагоре, за да отключите"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Плъзнете бързо нагоре, за да опитате отново"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Отключете, за да използвате NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Това устройство принадлежи на организацията ви"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Това устройство принадлежи на <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Плъзнете с пръст от иконата, за да използвате телефона"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Някои приложения използват <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" и "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> използва <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> наскоро използва <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративна версия)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефонно обаждане"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(чрез <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камерата"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"местополож."</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"микрофона"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 14c7779..437abb8 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"স্ক্রিন রেকর্ড"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"শুরু করুন"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"বন্ধ করুন"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"চালিয়ে যেতে, <b><xliff:g id="APP">%s</xliff:g></b> আপনার ডিভাইসের মাইক্রোফোন অ্যাক্সেস করতে চায়।"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"চালিয়ে যেতে, <b><xliff:g id="APP">%s</xliff:g></b> আপনার ডিভাইসের ক্যামেরা অ্যাক্সেস করতে চায়।"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"ডিভাইস"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"অন্য অ্যাপে যেতে উপরের দিকে সোয়াইপ করুন"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"একটি অ্যাপ ছেড়ে দ্রুত অন্য অ্যাপে যেতে ডান দিকে টেনে আনুন"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"খোলার জন্য আবার আলতো চাপুন"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"খোলার জন্য উপরে সোয়াইপ করুন"</string>
<string name="keyguard_retry" msgid="886802522584053523">"আবার চেষ্টা করতে উপরের দিকে সোয়াইপ করুন"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ব্যবহার করতে আনলক করুন"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"এই ডিভাইসটি <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>-এর"</string>
<string name="phone_hint" msgid="6682125338461375925">"ফোনের জন্য আইকন থেকে সোয়াইপ করুন"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"অ্যাপ্লিকেশনগুলি আপনার <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যবহার করছে।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" এবং "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> এখন <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যবহার করছে"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> সম্প্রতি <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যবহার করেছে"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"এন্টারপ্রাইজ"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ফোনকল"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>-এর মাধ্যমে)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ক্যামেরা"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"লোকেশন"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"মাইক্রোফোন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index d2f0c0d..f2b3edc 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -972,6 +972,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> koristi aplikaciju <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> je nedavno koristila aplikaciju <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(preduzeće)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski poziv"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(putem aplikacije <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kameru"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokaciju"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 1cc7288..bdcde31 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Gravació de pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Inicia"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Atura"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Per continuar, <b><xliff:g id="APP">%s</xliff:g></b> necessita accedir al micròfon del dispositiu."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Per continuar, <b><xliff:g id="APP">%s</xliff:g></b> necessita accedir a la càmera del dispositiu."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositiu"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Llisca cap amunt per canviar d\'aplicació"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrossega el dit cap a la dreta per canviar ràpidament d\'aplicació"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Torna a tocar per obrir-la."</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Llisca cap amunt per obrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Llisca cap a dalt per tornar-ho a provar"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloqueja per utilitzar l\'NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Aquest dispositiu pertany a la teva organització"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Aquest dispositiu pertany a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Llisca des de la icona per obrir el telèfon"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Algunes aplicacions estan fent servir el següent: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> està utilitzant: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Recentment <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ha utilitzat: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresa)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Trucada"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a través de: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"càmera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ubicació"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"micròfon"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 13fe676..aa5a1dd 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -420,10 +420,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Záznam obrazovky"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Spustit"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončit"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Než budete pokračovat, udělte aplikaci <b><xliff:g id="APP">%s</xliff:g></b> přístup k mikrofonu na zařízení."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Než budete pokračovat, udělte aplikaci <b><xliff:g id="APP">%s</xliff:g></b> přístup k fotoaparátu na zařízení."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Zařízení"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Přejetím nahoru přepnete aplikace"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Přetažením doprava rychle přepnete aplikace"</string>
@@ -446,8 +444,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Oznámení otevřete opětovným klepnutím"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Otevřete přejetím prstem nahoru"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Přejetím nahoru to zkusíte znovu"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC vyžaduje odemknutou obrazovku"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Toto zařízení patří vaší organizaci"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Toto zařízení patří organizaci <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefon otevřete přejetím prstem od ikony"</string>
@@ -980,6 +977,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikace využívají tato oprávnění: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" a "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikace <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> používá aplikaci <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikace <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nedávno použila aplikaci <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(podniková verze)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonní hovor"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prostřednictvím aplikace <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparát"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"poloha"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 18dfc06..5630293 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Optag skærm"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"<b><xliff:g id="APP">%s</xliff:g></b> skal have adgang til din enheds mikrofon, før den kan fortsætte."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"<b><xliff:g id="APP">%s</xliff:g></b> skal have adgang til din enheds kamera, før den kan fortsætte."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Enhed"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Stryg opad for at skifte apps"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Træk til højre for hurtigt at skifte app"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tryk igen for at åbne"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Stryg opad for at åbne"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Stryg opad for at prøve igen"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Lås op for at bruge NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Denne enhed tilhører din organisation"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Denne enhed tilhører <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Stryg fra telefonikonet"</string>
@@ -784,7 +781,7 @@
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Mellemrumstast"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
- <string name="keyboard_key_backspace" msgid="4095278312039628074">"Tilbagetast"</string>
+ <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Afspil/pause"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Stop"</string>
<string name="keyboard_key_media_next" msgid="8502476691227914952">"Næste"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps anvender enhedens <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" og "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> anvender <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> anvendte <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> for nylig"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(til virksomhedsbrug)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonopkald"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(via <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"placering"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 8485e84..e2468bf 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Bildschirmaufnahme"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Beenden"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Zum Fortfahren benötigt, <b><xliff:g id="APP">%s</xliff:g></b> Zugriff auf das Mikrofon deines Geräts."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Zum Fortfahren benötigt <b><xliff:g id="APP">%s</xliff:g></b> Zugriff auf die Kamera deines Geräts."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Gerät"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Nach oben wischen, um Apps zu wechseln"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Zum schnellen Wechseln der Apps nach rechts ziehen"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Erneut tippen, um Benachrichtigung zu öffnen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Zum Öffnen nach oben wischen"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Zum Wiederholen nach oben wischen"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Zur Verwendung von NFC entsperren"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Dieses Gerät gehört deiner Organisation"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Dieses Gerät gehört <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Zum Öffnen des Telefons vom Symbol wegwischen"</string>
@@ -970,6 +967,16 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps verwenden gerade Folgendes: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" und "</string>
+ <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
+ <skip />
<string name="privacy_type_camera" msgid="7974051382167078332">"Kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"Standort"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"Mikrofon"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index f21a9c9..aa747f2 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Εγγραφή οθόνης"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Έναρξη"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Διακοπή"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Για να συνεχίσετε, η εφαρμογή <b><xliff:g id="APP">%s</xliff:g></b&gt, χρειάζεται πρόσβαση στο μικρόφωνο της συσκευής σας."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Για να συνεχίσετε, η εφαρμογή <b><xliff:g id="APP">%s</xliff:g></b> χρειάζεται πρόσβαση στην κάμερα της συσκευής σας."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Συσκευή"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Σύρετε προς τα επάνω για εναλλαγή των εφαρμογών"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Σύρετε προς τα δεξιά για γρήγορη εναλλαγή εφαρμογών"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Πατήστε ξανά για να ανοίξετε"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Σύρετε προς τα επάνω για άνοιγμα"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Σύρετε προς τα πάνω για να δοκιμάσετε ξανά"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ξεκλείδωμα για χρήση του NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Αυτή η συσκευή ανήκει στον οργανισμό σας."</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Αυτή η συσκευή ανήκει στον οργανισμό <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Σύρετε προς τα έξω για τηλέφωνο"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Οι εφαρμογές χρησιμοποιούν τις λειτουργίες <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" και "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Χρήση <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> από την εφαρμογή <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Πρόσφατη χρήση <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> από την εφαρμογή <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(για επιχειρήσεις)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Τηλεφωνική κλήση"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(μέσω <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"κάμερα"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"τοποθεσία"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"μικρόφωνο"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 4ae38f5..88a40c3 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" and "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"location"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microphone"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index ff5e873e..bf7883a 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" and "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"location"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microphone"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 4ae38f5..88a40c3 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" and "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"location"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microphone"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 4ae38f5..88a40c3 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" and "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"location"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microphone"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index cddd75e..a14566a 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" and "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"location"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microphone"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 021d944..4b482b9 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hay aplicaciones que están usando tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" y "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> usó <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recientemente"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Teléfono"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a través de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"cámara"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ubicación"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"micrófono"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 584116e..36fee3f 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Grabar pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Detener"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> necesita tener acceso al micrófono del dispositivo."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> necesita tener acceso a la cámara del dispositivo."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Desliza el dedo hacia arriba para cambiar de aplicación"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrastra hacia la derecha para cambiar rápidamente de aplicación"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Toca de nuevo para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Desliza el dedo hacia arriba para volverlo a intentar"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquear para usar NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertenece a tu organización"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Desliza desde el icono para abrir el teléfono"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hay aplicaciones que usan tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" y "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando este elemento: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ha usado recientemente este elemento: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresa)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Llamada telefónica"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a través de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"cámara"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ubicación"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"micrófono"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 72c6907..ff043e0 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekraanisalvestus"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Alustage"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Peatage"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Jätkamiseks vajab rakendus <b><xliff:g id="APP">%s</xliff:g></b> juurdepääsu teie seadme mikrofonile."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Jätkamiseks vajab rakendus <b><xliff:g id="APP">%s</xliff:g></b> juurdepääsu teie seadme kaamerale."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Seade"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Rakenduste vahetamiseks pühkige üles"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Lohistage paremale, et rakendusi kiiresti vahetada"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Avamiseks puudutage uuesti"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Pühkige avamiseks üles"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Uuesti proovimiseks pühkige üles"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC kasutamiseks avage."</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"See seade kuulub teie organisatsioonile"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Selle seadme omanik on <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefoni kasutamiseks pühkige ikoonilt eemale"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Rakendused kasutavad järgmisi: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ja "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> kasutab järgmist: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> kasutas hiljuti järgmist: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ettevõte)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonikõne"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(üksuse <xliff:g id="ATTRIBUTION">%s</xliff:g> kaudu)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kaamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"asukoht"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index ab26b92..7517f17 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Pantaila-grabaketa"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hasi"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Gelditu"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Aurrera egiteko, gailuaren mikrofonoa atzitzeko baimena behar du <b><xliff:g id="APP">%s</xliff:g></b> aplikazioak."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Aurrera egiteko, gailuaren kamera atzitzeko baimena behar du <b><xliff:g id="APP">%s</xliff:g></b> aplikazioak."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Gailua"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Egin gora aplikazioa aldatzeko"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrastatu eskuinera aplikazioa azkar aldatzeko"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Irekitzeko, ukitu berriro"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Pasatu hatza gora irekitzeko"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Berriro saiatzeko, pasatu hatza gora"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desblokeatu NFC erabiltzeko"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Gailu hau zure erakundearena da"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Gailu hau <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> erakundearena da"</string>
<string name="phone_hint" msgid="6682125338461375925">"Pasatu hatza ikonotik, telefonoa irekitzeko"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikazio batzuk <xliff:g id="TYPES_LIST">%s</xliff:g> erabiltzen ari dira."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" eta "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> aplikazioa <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> erabiltzen ari da"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> aplikazioak <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> erabili du duela gutxi"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enpresa)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefono-deia"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> aplikazioaren bidez)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"kokapena"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofonoa"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index dccea5a..231eb45 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"برنامهها از <xliff:g id="TYPES_LIST">%s</xliff:g> شما استفاده میکنند."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" و "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> درحال استفاده از <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> است"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> اخیراً از <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> استفاده کرده است"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(شرکتی)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(ازطریق <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"دوربین"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"مکان"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"میکروفون"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 5b43cf9..a6c43d9 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Tallennus"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Aloita"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Lopeta"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Jotta voit jatkaa, <b><xliff:g id="APP">%s</xliff:g></b> tarvitsee pääsyn laitteesi mikrofoniin."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Jotta voit jatkaa, <b><xliff:g id="APP">%s</xliff:g></b> tarvitsee pääsyn laitteesi kameraan."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Laite"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Vaihda sovellusta pyyhkäisemällä ylös"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Vaihda sovellusta nopeasti vetämällä oikealle"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Avaa napauttamalla uudelleen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Avaa pyyhkäisemällä ylös"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Yritä uudelleen pyyhkäisemällä ylös"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Avaa lukitus käyttääksesi NFC:tä"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Organisaatiosi omistaa tämän laitteen"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g> omistaa tämän laitteen"</string>
<string name="phone_hint" msgid="6682125338461375925">"Avaa puhelu pyyhkäisemällä."</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"<xliff:g id="TYPES_LIST">%s</xliff:g> ovat sovellusten käytössä."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ja "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> käyttää kohdetta <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> käytti kohdetta <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> äskettäin"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(yritys)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Puhelu"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(kautta: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"sijainti"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofoni"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 2e36a92..07f8af5 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Des applications utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" et "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilise cet élément : <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> a récemment utilisé cet élément : <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(entreprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Appel téléphonique"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(par l\'intermédiaire de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"appareil photo"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"position"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microphone"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index b8a70ee..7bbbff6 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Enregistrement de l\'écran"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Pour continuer, <b><xliff:g id="APP">%s</xliff:g></b> a besoin d\'accéder au micro de votre appareil."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Pour continuer, <b><xliff:g id="APP">%s</xliff:g></b> a besoin d\'accéder à l\'appareil photo de votre appareil."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Appareil"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Balayer l\'écran vers le haut pour changer d\'application"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Déplacer vers la droite pour changer rapidement d\'application"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Appuyer à nouveau pour ouvrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Balayer vers le haut pour ouvrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Balayez l\'écran vers le haut pour réessayer"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Déverrouillez l\'écran pour pouvoir utiliser NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Cet appareil appartient à votre organisation"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Balayer pour téléphoner"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Des applications utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" et "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilise <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> a utilisé <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> récemment"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(Enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Appel téléphonique"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(via <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"appareil photo"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"position"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"micro"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 40a514e..0ac8261 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Gravación da pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Deter"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> precisa acceder ao micrófono do dispositivo."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> precisa acceder á cámara do dispositivo."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Pasar o dedo cara arriba para cambiar de aplicación"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrastra cara á dereita para cambiar de aplicacións rapidamente"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Toca de novo para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Pasa o dedo cara arriba para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Pasa o dedo cara arriba para tentalo de novo"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquea o dispositivo para utilizar a NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence á túa organización"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Pasa o dedo desde a icona para acceder ao teléfono"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hai aplicacións que están utilizando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está utilizando a aplicación <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilizou recentemente a aplicación <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(versión empresarial)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada de teléfono"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(mediante <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"a cámara"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"a localiz."</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"o micrófono"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 2c37ce7..f4e1333 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"સ્ક્રીન રેકૉર્ડ કરો"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"શરૂ કરો"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"રોકો"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"ચાલુ રાખવા માટે, <b><xliff:g id="APP">%s</xliff:g></b>ને તમારા ડિવાઇસના માઇક્રોફોનના ઍક્સેસની જરૂર છે."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"ચાલુ રાખવા માટે, <b><xliff:g id="APP">%s</xliff:g></b>ને તમારા ડિવાઇસના કૅમેરાના ઍક્સેસની જરૂર છે."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"ડિવાઇસ"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ઍપ સ્વિચ કરવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ઍપને ઝડપથી સ્વિચ કરવા માટે જમણે ખેંચો"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ખોલવા માટે ફરીથી ટૅપ કરો"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ખોલવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ફરી પ્રયાસ કરવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCનો ઉપયોગ કરવા માટે અનલૉક કરો"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>ની માલિકીનું છે"</string>
<string name="phone_hint" msgid="6682125338461375925">"ફોન માટે આયકનમાંથી સ્વાઇપ કરો"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ઍપ્લિકેશન તમારા <xliff:g id="TYPES_LIST">%s</xliff:g>નો ઉપયોગ કરી રહી છે."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" અને "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>નો ઉપયોગ કરી રહી છે"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>એ તાજેતરમાં જ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>નો ઉપયોગ કર્યો છે"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(એન્ટરપ્રાઇઝ)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ફોન કૉલ"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> મારફતે)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"કૅમેરા"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"સ્થાન"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"માઇક્રોફોન"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index af9bf1e..50b078f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -418,10 +418,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"स्क्रीन रिकॉर्ड"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"शुरू करें"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"रोकें"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"जारी रखने के लिए, <b><xliff:g id="APP">%s</xliff:g></b> को आपके डिवाइस का माइक्रोफ़ोन ऐक्सेस करने की ज़रूरत है."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"जारी रखने के लिए, <b><xliff:g id="APP">%s</xliff:g></b> को आपके डिवाइस का कैमरा ऐक्सेस करने की ज़रूरत है."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"डिवाइस"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ऐप्लिकेशन बदलने के लिए ऊपर स्वाइप करें"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ऐप्लिकेशन को झटपट स्विच करने के लिए उसे दाईं ओर खींचें और छोड़ें"</string>
@@ -444,8 +442,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"खोलने के लिए फिर से टैप करें"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"खोलने के लिए ऊपर स्वाइप करें"</string>
<string name="keyguard_retry" msgid="886802522584053523">"फिर से कोशिश करने के लिए ऊपर की ओर स्वाइप करें"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"एनएफ़सी इस्तेमाल करने के लिए, स्क्रीन को अनलॉक करें"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"इस डिवाइस का मालिकाना हक <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> के पास है"</string>
<string name="phone_hint" msgid="6682125338461375925">"फ़ोन के लिए आइकॉन से स्वाइप करें"</string>
@@ -972,6 +969,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ऐप्लिकेशन आपकी <xliff:g id="TYPES_LIST">%s</xliff:g> का इस्तेमाल कर रहे हैं."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" और "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> का इस्तेमाल कर रहा है"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ने हाल ही में <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> का इस्तेमाल किया"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"एंटरप्राइज़ वर्शन"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"<xliff:g id="ATTRIBUTION">%s</xliff:g> के ज़रिए"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"कैमरा"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"जगह"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"माइक्रोफ़ोन"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 73e1349..c80f262 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -418,10 +418,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Snimač zaslona"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Početak"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavi"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Da bi nastavila s radom, aplikacija <b><xliff:g id="APP">%s</xliff:g></b> treba pristupiti mikrofonu vašeg uređaja."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Da bi nastavila s radom, aplikacija <b><xliff:g id="APP">%s</xliff:g></b> treba pristupiti fotoaparatu vašeg uređaja."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Uređaj"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Prijeđite prstom prema gore da biste promijenili aplikaciju"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Povucite udesno da biste brzo promijenili aplikaciju"</string>
@@ -444,8 +442,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Dodirnite opet za otvaranje"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Prijeđite prstom prema gore da biste otvorili"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Prijeđite prstom prema gore za ponovni pokušaj"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Otključajte da biste upotrijebili NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada vašoj organizaciji"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Prijeđite prstom od ikone za telefon"</string>
@@ -975,6 +972,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije upotrebljavaju <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> koristi sljedeće: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nedavno je koristila sljedeće: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(za poslovne korisnike)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski poziv"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(putem aplikacije <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparat"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokaciju"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index c947754..868b2e7 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Képernyő rögzítése"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Indítás"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Leállítás"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"A folytatáshoz a(z) <b><xliff:g id="APP">%s</xliff:g></b> alkalmazásnak hozzáférésre van szüksége az eszköze mikrofonjához."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"A folytatáshoz a(z) <b><xliff:g id="APP">%s</xliff:g></b> alkalmazásnak hozzáférésre van szüksége az eszköze kamerájához."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Eszköz"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Váltás az alkalmazások között felfelé csúsztatással"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Húzza jobbra az ujját az alkalmazások közötti gyors váltáshoz"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Koppintson ismét a megnyitáshoz"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Csúsztasson felfelé a megnyitáshoz"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Az újrapróbálkozáshoz csúsztassa felfelé az ujját"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Az NFC használatához oldja fel a képernyőzárat"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Ez az eszköz az Ön szervezetének tulajdonában van"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Ez az eszköz a(z) <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> tulajdonában van"</string>
<string name="phone_hint" msgid="6682125338461375925">"A telefonhoz csúsztasson az ikonról"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Több alkalmazás használja a következőket: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" és "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"A(z) <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> használja a következőt: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"A(z) <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nemrég használta a következőt: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(vállalati)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonhívás"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a következőn keresztül: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"helyadatok"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 1ef540c..3c08874 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Էկրանի ձայնագրում"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Սկսել"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Կանգնեցնել"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Շարունակելու համար <b><xliff:g id="APP">%s</xliff:g></b> հավելվածին անհրաժեշտ է ձեր սարքի խոսափողի օգտագործման թույլտվություն։"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Շարունակելու համար <b><xliff:g id="APP">%s</xliff:g></b> հավելվածին անհրաժեշտ է ձեր սարքի տեսախցիկի օգտագործման թույլտվություն։"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Սարք"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Սահեցրեք վերև՝ մյուս հավելվածին անցնելու համար"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Քաշեք աջ՝ հավելվածների միջև անցնելու համար"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Կրկին հպեք՝ բացելու համար"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Բացելու համար սահեցրեք վերև"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Սահեցրեք վերև՝ նորից փորձելու համար"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ապակողպեք՝ NFC-ն օգտագործելու համար"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Այս սարքը պատկանում է ձեր կազմակերպությանը"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Այս սարքը պատկանում է «<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>» կազմակերպությանը"</string>
<string name="phone_hint" msgid="6682125338461375925">"Սահահարվածեք հեռախոսի պատկերակից"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Հավելվածներն օգտագործում են ձեր <xliff:g id="TYPES_LIST">%s</xliff:g>:"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" և "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> հավելվածն օգտագործում է «<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>» գործառույթը"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> հավելվածը վերջերս օգտագործել է «<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>» գործառույթը"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(կորպորատիվ տարբերակ)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Հեռախոսազանգ"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>-ի միջոցով)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"տեսախցիկը"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"վայրը"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"խոսափողը"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 0de7efa..a5d8a27 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Rekaman Layar"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mulai"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Untuk melanjutkan, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses ke mikrofon perangkat."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Untuk melanjutkan, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses ke kamera perangkat."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Perangkat"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Geser ke atas untuk beralih aplikasi"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Tarik ke kanan untuk beralih aplikasi dengan cepat"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ketuk lagi untuk membuka"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Geser ke atas untuk membuka"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Geser ke atas untuk mencoba lagi"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Buka kunci untuk menggunakan NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Perangkat ini milik organisasi Anda"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Perangkat ini milik <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Geser dari ikon untuk telepon"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" dan "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> sedang menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> baru-baru ini"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(perusahaan)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Panggilan telepon"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(melalui <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokasi"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 777479d..5f794e7 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Skjáupptaka"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hefja"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stöðva"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Til að halda áfram þarf <b><xliff:g id="APP">%s</xliff:g></b> aðgang að hljóðnema tækisins."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Til að halda áfram þarf <b><xliff:g id="APP">%s</xliff:g></b> aðgang að myndavél tækisins."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Tæki"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Strjúktu upp til að skipta á milli forrita"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Dragðu til hægri til að skipta hratt á milli forrita"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ýttu aftur til að opna"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Strjúktu upp til að opna"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Strjúktu upp til að reyna aftur"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Taktu úr lás til að nota NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Þetta tæki tilheyrir fyrirtækinu þínu"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Þetta tæki tilheyrir <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Strjúktu frá tákninu fyrir síma"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Forrit eru að nota <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" og "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> er að nota <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> notaði <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> nýlega"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(fyrirtæki)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Símtal"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(í gegnum <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"myndavél"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"staðsetning"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"hljóðnemi"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 2af2d5f..cfd0797 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Registrazione schermo"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Inizia"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Interrompi"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Per continuare, l\'app <b><xliff:g id="APP">%s</xliff:g></b> deve accedere al microfono del dispositivo."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Per continuare, l\'app <b><xliff:g id="APP">%s</xliff:g></b> deve accedere alla videocamera del dispositivo."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Scorri verso l\'alto per passare ad altre app"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Trascina verso destra per cambiare velocemente app"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tocca ancora per aprire"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Scorri verso l\'alto per aprire"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Scorri verso l\'alto per riprovare"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Sblocca per usare NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Questo dispositivo appartiene alla tua organizzazione"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Questo dispositivo appartiene a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Scorri per accedere al telefono"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> sta usando: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ha usato di recente: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(tramite <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"Fotocamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"luogo"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"un microfono"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index e8a1ede..c4d088d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -420,10 +420,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"הקלטת המסך"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"התחלה"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"עצירה"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"כדי להמשיך, האפליקציה <b><xliff:g id="APP">%s</xliff:g></b> צריכה גישה למיקרופון של המכשיר שלך."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"כדי להמשיך, האפליקציה <b><xliff:g id="APP">%s</xliff:g></b> צריכה גישה למצלמה של המכשיר שלך."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"מכשיר"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"יש להחליק מעלה כדי להחליף אפליקציות"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"יש לגרור ימינה כדי לעבור במהירות בין אפליקציות"</string>
@@ -446,8 +444,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"הקש שוב כדי לפתוח"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"צריך להחליק כדי לפתוח"</string>
<string name="keyguard_retry" msgid="886802522584053523">"יש להחליק למעלה כדי לנסות שוב"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"יש לבטל נעילה כדי להשתמש ב-NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"המכשיר הזה שייך לארגון שלך"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"החלק מהסמל כדי להפעיל את הטלפון"</string>
@@ -980,6 +977,16 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"אפליקציות משתמשות ב<xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" וגם "</string>
+ <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
+ <skip />
<string name="privacy_type_camera" msgid="7974051382167078332">"מצלמה"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"מיקום"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"מיקרופון"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index e15366c..3fcc713 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"スクリーン レコード"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"続行するには、<b><xliff:g id="APP">%s</xliff:g></b> にデバイスのマイクへのアクセスを許可する必要があります。"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"続行するには、<b><xliff:g id="APP">%s</xliff:g></b> にデバイスのカメラへのアクセスを許可する必要があります。"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"デバイス"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"アプリを切り替えるには上にスワイプ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"右にドラッグするとアプリを素早く切り替えることができます"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"開くにはもう一度タップしてください"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"開くには上にスワイプします"</string>
<string name="keyguard_retry" msgid="886802522584053523">"上にスワイプしてもう一度お試しください"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC を使用するには、ロックを解除してください"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"これは組織が所有するデバイスです"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"これは <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> が所有するデバイスです"</string>
<string name="phone_hint" msgid="6682125338461375925">"右にスワイプして通話"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"アプリは<xliff:g id="TYPES_LIST">%s</xliff:g>を使用しています。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 、 "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> は <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> を使用しています"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> は最近 <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> を使用しました"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(エンタープライズ版)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"電話"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> 経由)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"カメラ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"現在地情報"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"マイク"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 386847e..5c11fa4 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ეკრანის ჩანაწერი"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"დაწყება"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"შეწყვეტა"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"გასაგრძელებლად <b><xliff:g id="APP">%s</xliff:g></b>-ს თქვენი მოწყობილობის მიკროფონზე წვდომა სჭირდება."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"გასაგრძელებლად <b><xliff:g id="APP">%s</xliff:g></b>-ს თქვენი მოწყობილობის კამერაზე წვდომა სჭირდება."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"მოწყობილობა"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"გადაფურცლეთ ზემოთ აპების გადასართავად"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"აპების სწრაფად გადასართავად ჩავლებით გადაიტანეთ მარჯვნივ"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"შეეხეთ ისევ გასახსნელად"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"გასახსნელად გადაფურცლეთ ზემოთ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ხელახლა საცდელად გადაფურცლეთ ზემოთ"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"განბლოკეთ NFC-ის გამოსაყენებლად"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ამ მოწყობილობას ფლობს <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"ტელეფონისთვის გადაფურცლეთ ხატულადან"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"აპლიკაციების მიერ გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" და "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> იყენებს აპს <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"აპმა <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ახლახან გამოიყენა <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(კორპორაციული)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"სატელეფონო ზარი"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(აპის <xliff:g id="ATTRIBUTION">%s</xliff:g> მეშვეობით)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"კამერა"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"მდებარეობა"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"მიკროფონი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index db318ea..89f905733f 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -285,8 +285,8 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Жұмыс режимі қосулы."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="6256690740556798683">"Жұмыс режимі өшірілді."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Жұмыс режимі қосылды."</string>
- <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver өшірілді."</string>
- <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver қосылды."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Трафикті үнемдеу режимі өшірілді."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Трафикті үнемдеу режимі қосылды."</string>
<string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="7608378211873807353">"Sensor Privacy функциясы өшірулі."</string>
<string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="4267393685085328801">"Sensor Privacy функциясы қосулы."</string>
<string name="accessibility_brightness" msgid="5391187016177823721">"Дисплей жарықтығы"</string>
@@ -383,7 +383,7 @@
<string name="quick_settings_tethering_label" msgid="5257299852322475780">"Тетеринг"</string>
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Хотспот"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Қосылуда…"</string>
- <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Data Saver қосулы"</string>
+ <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Трафикті үнемдеу режимі қосулы"</string>
<plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
<item quantity="other">%d құрылғы</item>
<item quantity="one">%d құрылғы</item>
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Экранды жазу"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Бастау"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Тоқтату"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Жалғастыру үшін <b><xliff:g id="APP">%s</xliff:g></b> қолданбасы құрылғыңыздың микрофонына рұқсат алу керек."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Жалғастыру үшін <b><xliff:g id="APP">%s</xliff:g></b> қолданбасы құрылғыңыздың камерасына рұқсат алу керек."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Құрылғы"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Қолданбалар арасында ауысу үшін жоғары сырғытыңыз"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Қолданбаларды жылдам ауыстырып қосу үшін оңға қарай сүйреңіз"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ашу үшін қайта түртіңіз"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ашу үшін жоғары қарай сырғытыңыз."</string>
<string name="keyguard_retry" msgid="886802522584053523">"Әрекетті қайталау үшін жоғары сырғытыңыз."</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC пайдалану үшін құлыпты ашыңыз."</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Бұл құрылғы ұйымыңызға тиесілі."</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Бұл құрылғы <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ұйымына тиесілі."</string>
<string name="phone_hint" msgid="6682125338461375925">"Телефонды ашу үшін белгішеден әрі қарай сырғытыңыз"</string>
@@ -825,9 +822,9 @@
<string name="accessibility_long_click_tile" msgid="210472753156768705">"Параметрлерді ашу"</string>
<string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"Құлақаспап қосылды"</string>
<string name="accessibility_status_bar_headset" msgid="2699275863720926104">"Құлақаспап жинағы қосылды"</string>
- <string name="data_saver" msgid="3484013368530820763">"Data Saver"</string>
- <string name="accessibility_data_saver_on" msgid="5394743820189757731">"Дерек сақтағыш қосулы"</string>
- <string name="accessibility_data_saver_off" msgid="58339669022107171">"Дерек сақтағышы өшірулі"</string>
+ <string name="data_saver" msgid="3484013368530820763">"Трафикті үнемдеу"</string>
+ <string name="accessibility_data_saver_on" msgid="5394743820189757731">"Трафикті үнемдеу режимі қосулы"</string>
+ <string name="accessibility_data_saver_off" msgid="58339669022107171">"Трафикті үнемдеу режимі өшірулі"</string>
<string name="switch_bar_on" msgid="1770868129120096114">"Қосулы"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Өшірулі"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Қолжетімді емес"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Қолданбаларда <xliff:g id="TYPES_LIST">%s</xliff:g> пайдаланылуда."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" және "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> қолданбасы қазір <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> пайдаланады"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> қолданбасы жақында <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> пайдаланды"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративтік)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефон қоңырауы"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> арқылы)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"геодерек"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"микрофон"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index dca2623..8555707 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ការថតអេក្រង់"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ចាប់ផ្ដើម"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ឈប់"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"ដើម្បីបន្ត <b><xliff:g id="APP">%s</xliff:g></b> ត្រូវការសិទ្ធិចូលប្រើមីក្រូហ្វូនរបស់ឧបករណ៍អ្នក។"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"ដើម្បីបន្ត <b><xliff:g id="APP">%s</xliff:g></b> ត្រូវការសិទ្ធិចូលប្រើកាមេរ៉ារបស់ឧបករណ៍អ្នក។"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"ឧបករណ៍"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"អូសឡើងលើដើម្បីប្តូរកម្មវិធី"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"អូសទៅស្ដាំដើម្បីប្ដូរកម្មវិធីបានរហ័ស"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ប៉ះម្ដងទៀត ដើម្បីបើក"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"អូសឡើងលើដើម្បីបើក"</string>
<string name="keyguard_retry" msgid="886802522584053523">"អូសឡើងលើ ដើម្បីព្យាយាមម្ដងទៀត"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"ដោះសោ ដើម្បីប្រើ NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ឧបករណ៍នេះគឺជាកម្មសិទ្ធិរបស់ស្ថាប័នអ្នក"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ឧបករណ៍នេះគឺជាកម្មសិទ្ធិរបស់ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"អូសចេញពីរូបតំណាងដើម្បីប្រើទូរស័ព្ទ"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"កម្មវិធីកំពុងប្រើ <xliff:g id="TYPES_LIST">%s</xliff:g> របស់អ្នក។"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" និង "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> កំពុងប្រើ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> បានប្រើ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ថ្មីៗនេះ"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(សហគ្រាស)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ការហៅទូរសព្ទ"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(តាមរយៈ <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"កាមេរ៉ា"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ទីតាំង"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"មីក្រូហ្វូន"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index e08a296..c920cc2 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ನಿಲ್ಲಿಸಿ"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"ಮುಂದುವರಿಯಲು, <b><xliff:g id="APP">%s</xliff:g></b> ಗೆ ನಿಮ್ಮ ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ನ ಪ್ರವೇಶದ ಅಗತ್ಯವಿದೆ."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"ಮುಂದುವರಿಯಲು, <b><xliff:g id="APP">%s</xliff:g></b> ಗೆ ನಿಮ್ಮ ಸಾಧನದ ಕ್ಯಾಮರಾದ ಪ್ರವೇಶದ ಅಗತ್ಯವಿದೆ."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"ಸಾಧನ"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಬದಲಿಸಲು ತ್ವರಿತವಾಗಿ ಬಲಕ್ಕೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ತೆರೆಯಲು ಮತ್ತೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ತೆರೆಯಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ಬಳಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ಈ ಸಾಧನವು ನಿಮ್ಮ ಸಂಸ್ಥೆಗೆ ಸೇರಿದೆ"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ಈ ಸಾಧನವು <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ಗೆ ಸೇರಿದೆ"</string>
<string name="phone_hint" msgid="6682125338461375925">"ಫೋನ್ಗಾಗಿ ಐಕಾನ್ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ನಿಮ್ಮ <xliff:g id="TYPES_LIST">%s</xliff:g> ಅನ್ನು ಆ್ಯಪ್ಗಳು ಬಳಸುತ್ತಿವೆ."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ಮತ್ತು "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ಅನ್ನು ಬಳಸುತ್ತಿದೆ"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ಅನ್ನು ಬಳಸಿದೆ"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ಎಂಟರ್ಪ್ರೈಸ್)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ಫೋನ್ ಕರೆ"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ಮೂಲಕ)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ಕ್ಯಾಮರಾ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ಸ್ಥಳ"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"ಮೈಕ್ರೋಫೋನ್"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 2b6e2d5..362c0bc 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"화면 녹화"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"시작"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"중지"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"계속하려면 <b><xliff:g id="APP">%s</xliff:g></b>에서 기기 마이크에 액세스해야 합니다."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"계속하려면 <b><xliff:g id="APP">%s</xliff:g></b>에서 기기 카메라에 액세스해야 합니다."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"기기"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"위로 스와이프하여 앱 전환"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"앱을 빠르게 전환하려면 오른쪽으로 드래그"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"다시 탭하여 열기"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"위로 스와이프하여 열기"</string>
<string name="keyguard_retry" msgid="886802522584053523">"위로 스와이프하여 다시 시도해 주세요"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"잠금 해제하여 NFC 사용"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"내 조직에 속한 기기입니다."</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>에 속한 기기입니다."</string>
<string name="phone_hint" msgid="6682125338461375925">"전화 기능을 사용하려면 아이콘에서 스와이프하세요."</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"애플리케이션이 <xliff:g id="TYPES_LIST">%s</xliff:g>을(를) 사용 중입니다."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 및 "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>에서 <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> 사용 중"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>에서 최근에 <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>을(를) 사용함"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(기업용)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"전화 통화"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> 사용)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"카메라"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"위치"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"마이크"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index ffb66f9..fe69443 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -418,10 +418,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Экранды жаздыруу"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Баштадык"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Токтотуу"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Улантуу үчүн <b><xliff:g id="APP">%s</xliff:g></b> колдонмосуна түзмөгүңүздүн микрофонун пайдаланууга уруксат беришиңиз керек."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Улантуу үчүн <b><xliff:g id="APP">%s</xliff:g></b> колдонмосуна түзмөгүңүздүн камерасын пайдаланууга уруксат беришиңиз керек."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Түзмөк"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Башка колдонмого которулуу үчүн,, өйдө сүрүңүз"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Колдонмолорду тез которуштуруу үчүн, оңго сүйрөңүз"</string>
@@ -444,8 +442,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ачуу үчүн кайра таптап коюңуз"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ачуу үчүн өйдө сүрүңүз"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Кайталоо үчүн экранды өйдө сүрүңүз"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC технологиясын колдонуу үчүн кулпуcун ачыңыз"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Бул түзмөк уюмуңузга таандык"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Бул түзмөк төмөнкүгө таандык: <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Сүрөтчөнү серпип телефонго өтүңүз"</string>
@@ -972,6 +969,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Колдонмолор төмөнкүлөрдү пайдаланып жатышат: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" жана "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> колдонуп жатат"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Жакында <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> колдонулду"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративдик)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефон чалуу"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> аркылуу)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"жайгашкан жер"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"микрофон"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 6e367a1..e5e89e44 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ການບັນທຶກໜ້າຈໍ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ເລີ່ມ"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ຢຸດ"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"ເພື່ອດຳເນີນການຕໍ່, <b><xliff:g id="APP">%s</xliff:g></b> ຕ້ອງການສິດເຂົ້າເຖິງໄມໂຄຣໂຟນອຸປະກອນຂອງທ່ານ."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"ເພື່ອດຳເນີນການຕໍ່, <b><xliff:g id="APP">%s</xliff:g></b> ຕ້ອງການສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຂອງອຸປະກອນທ່ານ."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"ອຸປະກອນ"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ປັດຂື້ນເພື່ອສະຫຼັບແອັບ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ລາກໄປຂວາເພື່ອສະຫຼັບແອັບດ່ວນ"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ແຕະອີກຄັ້ງເພື່ອເປີດ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ປັດຂຶ້ນເພື່ອເປີດ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ປັດຂຶ້ນເພື່ອລອງໃໝ່"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"ປົດລັອກເພື່ອໃຊ້ NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ອຸປະກອນນີ້ເປັນຂອງອົງການທ່ານ"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ອຸປະກອນນີ້ເປັນຂອງ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"ປັດຈາກໄອຄອນສຳລັບໂທລະສັບ"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ແອັບພລິເຄຊັນກຳລັງໃຊ້ <xliff:g id="TYPES_LIST">%s</xliff:g> ຂອງທ່ານ."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ແລະ "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ກຳລັງໃຊ້ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ຢູ່"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ໃຊ້ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ເມື່ອບໍ່ດົນມານີ້"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ອົງກອນ)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ໂທລະສັບ"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(ຜ່ານ <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ກ້ອງຖ່າຍຮູບ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ສະຖານທີ່"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"ໄມໂຄຣໂຟນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 34275f4..a5de095 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -420,10 +420,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekrano įrašas"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Pradėti"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stabdyti"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Kad būtų galima tęsti, <b><xliff:g id="APP">%s</xliff:g></b> reikalinga prieiga prie įrenginio mikrofono."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Kad būtų galima tęsti, <b><xliff:g id="APP">%s</xliff:g></b> reikalinga prieiga prie įrenginio fotoaparato."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Įrenginys"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Perbraukite aukštyn, kad perjungtumėte programas"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Vilkite į dešinę, kad greitai perjungtumėte programas"</string>
@@ -446,8 +444,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Palieskite dar kartą, kad atidarytumėte"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Perbraukite aukštyn, kad atidarytumėte"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Jei norite bandyti dar kartą, perbraukite aukštyn"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Norėdami naudoti NFC, atrakinkite"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Šis įrenginys priklauso jūsų organizacijai"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Šis įrenginys priklauso „<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>“"</string>
<string name="phone_hint" msgid="6682125338461375925">"Perbraukite iš telefono piktogramos"</string>
@@ -980,6 +977,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programos naudoja: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ir "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Programa „<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>“ naudoja: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Programa „<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>“ neseniai naudojo: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(įmonės versija)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefono skambutis"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(naud. <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparatą"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"vietovę"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofoną"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 2d427b7..019810f 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -418,10 +418,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekrāna ierakstīšana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Sākt"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Apturēt"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Lai turpinātu, lietotnei <b><xliff:g id="APP">%s</xliff:g></b> nepieciešama piekļuve jūsu ierīces mikrofonam."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Lai turpinātu, lietotnei <b><xliff:g id="APP">%s</xliff:g></b> nepieciešama piekļuve jūsu ierīces kamerai."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Ierīce"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Velciet augšup, lai pārslēgtu lietotnes"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Lai ātri pārslēgtu lietotnes, velciet pa labi"</string>
@@ -444,8 +442,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Pieskarieties vēlreiz, lai atvērtu"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Velciet augšup, lai atvērtu"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Velciet augšup, lai mēģinātu vēlreiz"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Atbloķējiet ierīci, lai izmantotu NFC."</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Šī ierīce pieder jūsu organizācijai."</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Šī ierīce pieder organizācijai <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
<string name="phone_hint" msgid="6682125338461375925">"Lai lietotu tālruni, velciet no ikonas"</string>
@@ -975,6 +972,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Lietojumprogrammas izmanto šādas funkcijas: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" un "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> pašlaik izmanto šādu darbību: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nesen izmantoja šādu darbību: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(uzņēmumiem)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Tālruņa zvaniem"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(izmantojot lietotni <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"atrašanās vieta"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofons"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 17d6e10..c124eef 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Снимање екран"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Започни"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Сопри"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"За да продолжи, на <b><xliff:g id="APP">%s</xliff:g></b> ѝ е потребен пристап до микрофонот на уредот."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"За да продолжи, на <b><xliff:g id="APP">%s</xliff:g></b> ѝ е потребен пристап до камерата на уредот."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Уред"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Повлечете нагоре за да се префрлите од една на друга апликација"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Повлечете надесно за брзо префрлање меѓу апликациите"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Допрете повторно за да се отвори"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Повлечете за да отворите"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Повлечете нагоре за да се обидете повторно"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Отклучете за да користите NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Уредов е во сопственост на организацијата"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Уредов е во сопственост на <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Повлечете од иконата за телефонот"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Апликациите користат <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" и "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> користи <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> користеше <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> неодамна"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(претпријатие)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефонски повик"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(преку <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"локација"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"микрофон"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 916fbe1..965f4df 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"സ്ക്രീൻ റെക്കോർഡ്"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ആരംഭിക്കുക"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"നിര്ത്തുക"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"തുടരാൻ, <b><xliff:g id="APP">%s</xliff:g></b> ആപ്പിന് നിങ്ങളുടെ ഉപകരണത്തിന്റെ മൈക്രോഫോണിലേക്ക് ആക്സസ് നൽകേണ്ടതുണ്ട്."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"തുടരാൻ, <b><xliff:g id="APP">%s</xliff:g></b> ആപ്പിന് നിങ്ങളുടെ ഉപകരണത്തിന്റെ ക്യാമറയിലേക്ക് ആക്സസ് നൽകേണ്ടതുണ്ട്."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"ഉപകരണം"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ആപ്പുകൾ മാറാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ആപ്പുകൾ പെട്ടെന്ന് മാറാൻ വലത്തോട്ട് വലിച്ചിടുക"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"തുറക്കുന്നതിന് വീണ്ടും ടാപ്പുചെയ്യുക"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"തുറക്കാൻ മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്യുക"</string>
<string name="keyguard_retry" msgid="886802522584053523">"വീണ്ടും ശ്രമിക്കാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ഉപയോഗിക്കാൻ അൺലോക്ക് ചെയ്യുക"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റേതാണ്"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ഈ ഉപകരണം <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> എന്ന സ്ഥാപനത്തിന്റേതാണ്"</string>
<string name="phone_hint" msgid="6682125338461375925">"ഫോൺ ഐക്കണിൽ നിന്ന് സ്വൈപ്പുചെയ്യുക"</string>
@@ -970,6 +967,16 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ആപ്പുകൾ നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%s</xliff:g> ഉപയോഗിക്കുന്നു."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" കൂടാതെ "</string>
+ <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
+ <skip />
<string name="privacy_type_camera" msgid="7974051382167078332">"ക്യാമറ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ലൊക്കേഷന്"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"മൈക്രോഫോൺ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 38bb386..400352b 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Дэлгэцийн бичлэг хийх"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Эхлүүлэх"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зогсоох"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Үргэлжлүүлэхийн тулд <b><xliff:g id="APP">%s</xliff:g></b> таны төхөөрөмжийн микрофонд хандах шаардлагатай."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Үргэлжлүүлэхийн тулд <b><xliff:g id="APP">%s</xliff:g></b> таны төхөөрөмжийн камерт хандах шаардлагатай."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Төхөөрөмж"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Апп сэлгэхийн тулд дээш шударна уу"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Аппуудыг хурдан сэлгэхийн тулд баруун тийш чирнэ үү"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Нээхийн тулд дахин товшино уу"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Нээхийн тулд дээш шударна уу"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Дахин оролдохын тулд дээш шударна уу"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC-г ашиглахын тулд түгжээг тайлна уу"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Энэ төхөөрөмж танай байгууллагад харьяалагддаг"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Энэ төхөөрөмж <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>-д харьяалагддаг"</string>
<string name="phone_hint" msgid="6682125338461375925">"Утсыг гаргахын тулд дүрс тэмдгээс шудрах"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Аппууд таны <xliff:g id="TYPES_LIST">%s</xliff:g>-г ашиглаж байна."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" болон "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-г ашиглаж байна"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-г саяхан ашигласан"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(байгууллага)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Утасны дуудлага"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>-р)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камер"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"байршил"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"микрофон"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 1c9cbd2..fe1c910 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -344,10 +344,8 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"इनपुट पद्धत"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"स्थान बंद"</string>
- <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
- <skip />
- <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
- <skip />
+ <string name="quick_settings_camera_label" msgid="1367149596242401934">"कॅमेरा ब्लॉक करा"</string>
+ <string name="quick_settings_mic_label" msgid="8245831073612564953">"मायक्रोफोन म्यूट करा"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"मीडिया डिव्हाइस"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"फक्त आणीबाणीचे कॉल"</string>
@@ -418,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"स्क्रीन रेकॉर्ड"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"सुरू"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"थांबा"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"पुढे सुरू ठेवण्यासाठी, <b><xliff:g id="APP">%s</xliff:g></b> ला तुमच्या डिव्हाइसचा मायक्रोफोन अॅक्सेस करण्याची आवश्यकता आहे."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"पुढे सुरू ठेवण्यासाठी, <b><xliff:g id="APP">%s</xliff:g></b> ला तुमच्या डिव्हाइसचा कॅमेरा अॅक्सेस करण्याची आवश्यकता आहे."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"डिव्हाइस"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"अॅप्स स्विच करण्यासाठी वर स्वाइप करा"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"अॅप्स वर झटपट स्विच करण्यासाठी उजवीकडे ड्रॅग करा"</string>
@@ -444,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"उघडण्यासाठी पुन्हा टॅप करा"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"उघडण्यासाठी वर स्वाइप करा"</string>
<string name="keyguard_retry" msgid="886802522584053523">"पुन्हा प्रयत्न करण्यासाठी वर स्वाइप करा"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC वापरण्यासाठी स्क्रीन अनलॉक करा"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"हे डिव्हाइस तुमच्या संस्थेचे आहे"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> चे आहे"</string>
<string name="phone_hint" msgid="6682125338461375925">"फोनसाठी चिन्हावरून स्वाइप करा"</string>
@@ -972,6 +967,16 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ॲप्लिकेशन्स तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" आणि "</string>
+ <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
+ <skip />
<string name="privacy_type_camera" msgid="7974051382167078332">"कॅमेरा"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"स्थान"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"मायक्रोफोन"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 4751f40..b1d0b47 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Rakam Skrin"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mula"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Untuk meneruskan proses, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses kepada mikrofon peranti anda."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Untuk meneruskan proses, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses kepada kamera peranti anda."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Peranti"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Leret ke atas untuk menukar apl"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Seret ke kanan untuk beralih apl dengan pantas"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ketik lagi untuk membuka"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Leret ke atas untuk buka"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Leret ke atas untuk mencuba lagi"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Buka kunci untuk menggunakan NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Peranti ini milik organisasi anda"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Peranti ini milik <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Leret dari ikon untuk telefon"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" dan "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> sedang menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> baru-baru ini"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(perusahaan)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Panggilan telefon"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(melalui <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokasi"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index abd2b23..42b5b23 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ဖန်သားပြင် မှတ်တမ်းတင်ရန်"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"စတင်ရန်"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ရပ်ရန်"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"ဆက်လက်လုပ်ဆောင်ရန် <b><xliff:g id="APP">%s</xliff:g></b> က သင့်စက်၏ မိုက်ခရိုဖုန်းကို အသုံးပြုခွင့်ရရန် လိုအပ်သည်။"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"ဆက်လက်လုပ်ဆောင်ရန် <b><xliff:g id="APP">%s</xliff:g></b> က သင့်စက်၏ ကင်မရာကို အသုံးပြုခွင့်ရရန် လိုအပ်သည်။"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"စက်"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"အက်ပ်များကို ဖွင့်ရန် အပေါ်သို့ ပွတ်ဆွဲပါ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"အက်ပ်များကို ပြောင်းရန် ညာဘက်သို့ ဖိဆွဲပါ"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ဖွင့်ရန် ထပ်ပြီး ပုတ်ပါ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ဖွင့်ရန် အပေါ်သို့ပွတ်ဆွဲပါ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ထပ်စမ်းကြည့်ရန် အပေါ်သို့ပွတ်ဆွဲပါ"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ကို အသုံးပြုရန် လော့ခ်ဖွင့်ပါ"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ဤစက်ကို သင့်အဖွဲ့အစည်းက ပိုင်ဆိုင်သည်"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ဤစက်ကို <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> က ပိုင်ဆိုင်သည်"</string>
<string name="phone_hint" msgid="6682125338461375925">"ဖုန်းအတွက် သင်္ကေတပုံအား ပွတ်ဆွဲပါ"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"အပလီကေးရှင်းများက သင်၏ <xliff:g id="TYPES_LIST">%s</xliff:g> ကို အသုံးပြုနေသည်။"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"၊ "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" နှင့် "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> က <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ကို အသုံးပြုနေသည်"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> က <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ကို မကြာသေးမီက အသုံးပြုထားသည်"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(လုပ်ငန်းသုံး)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ဖုန်းခေါ်ဆိုမှု"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> မှတစ်ဆင့်)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ကင်မရာ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"တည်နေရာ"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"မိုက်ခရိုဖုန်း"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index e914399..ef48f8f 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Skjermopptak"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stopp"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"For å fortsette må <b><xliff:g id="APP">%s</xliff:g></b> ha tilgang til enhetsmikrofonen."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"For å fortsette må <b><xliff:g id="APP">%s</xliff:g></b> ha tilgang til enhetskameraet."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Enhet"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Sveip opp for å bytte apper"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Dra til høyre for å bytte apper raskt"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Trykk på nytt for å åpne"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Sveip opp for å åpne"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Sveip opp for å prøve igjen"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Lås opp for å bruke NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Denne enheten tilhører organisasjonen din"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Denne enheten tilhører <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Sveip ikonet for å åpne telefon"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apper bruker <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" og "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> bruker <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> har brukt <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> nylig"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonsamtale"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(til og med <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"posisjon"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index ad087c5..2594e8c 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"स्रिनको रेकर्ड"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"सुरु गर्नुहोस्"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"रोक्नुहोस्"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"जारी राख्न <b><xliff:g id="APP">%s</xliff:g></b> लाई तपाईंको यन्त्रको माइक्रोफोन प्रयोग गर्ने अनुमति दिनु पर्ने हुन्छ।"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"जारी राख्न <b><xliff:g id="APP">%s</xliff:g></b> लाई तपाईंको यन्त्रको क्यामेरा प्रयोग गर्ने अनुमति दिनु पर्ने हुन्छ।"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"यन्त्र"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"एपहरू बदल्न माथितिर स्वाइप गर्नुहोस्"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"एपहरू बदल्न द्रुत गतिमा दायाँतिर ड्र्याग गर्नुहोस्"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"खोल्न पुनः ट्याप गर्नुहोस्"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"खोल्न माथितिर स्वाइप गर्नुहोस्"</string>
<string name="keyguard_retry" msgid="886802522584053523">"फेरि प्रयास गर्न माथितिर स्वाइप गर्नुहोस्"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC प्रयोग गर्न स्क्रिन अनलक गर्नुहोस्"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"यो यन्त्र तपाईंको सङ्गठनको स्वामित्वमा छ"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"यो यन्त्र <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> को स्वामित्वमा छ"</string>
<string name="phone_hint" msgid="6682125338461375925">"फोनको लागि आइकनबाट स्वाइप गर्नुहोस्"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"एपहरूले तपाईंको <xliff:g id="TYPES_LIST">%s</xliff:g> प्रयोग गर्दै छन्।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" र "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ले <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> प्रयोग गरिरहेको छ"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ले हालसालै <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> प्रयोग गरेको छ"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(इन्टरप्राइज)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"फोन कल"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> मार्फत)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"क्यामेरा"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"स्थान"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"माइक्रोफोन"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 614d374..0274132 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Schermopname"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppen"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"<b><xliff:g id="APP">%s</xliff:g></b> heeft toegang tot de microfoon van je apparaat nodig om door te gaan."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"<b><xliff:g id="APP">%s</xliff:g></b> heeft toegang tot de camera van je apparaat nodig om door te gaan."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Apparaat"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swipe omhoog om te schakelen tussen apps"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Sleep naar rechts om snel tussen apps te schakelen"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tik nog eens om te openen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe omhoog om te openen"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Swipe omhoog om het opnieuw te proberen"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ontgrendel het apparaat om NFC te gebruiken"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Dit apparaat is eigendom van je organisatie"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Dit apparaat is eigendom van <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Swipen voor telefoon"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps gebruiken je <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" en "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> gebruikt de <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> heeft de <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recent gebruikt"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(zakelijke versie)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefoongesprek"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(via <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"locatie"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microfoon"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 97af29b..16d8a296 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ସ୍କ୍ରିନ୍ ରେକର୍ଡ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ବନ୍ଦ କରନ୍ତୁ"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"ଜାରି ରଖିବାକୁ, <b><xliff:g id="APP">%s</xliff:g></b> ଆପଣଙ୍କ ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଆକ୍ସେସ୍ ଆବଶ୍ୟକ କରେ।"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"ଜାରି ରଖିବାକୁ, <b><xliff:g id="APP">%s</xliff:g></b> ଆପଣଙ୍କ ଡିଭାଇସର କ୍ୟାମେରାକୁ ଆକ୍ସେସ୍ ଆବଶ୍ୟକ କରେ।"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"ଡିଭାଇସ୍"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ଆପ୍କୁ ବଦଳ କରିବା ପାଇଁ ସ୍ଵାଇପ୍ କରନ୍ତୁ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ଆପ୍ଗୁଡ଼ିକ ମଧ୍ୟରେ ଶୀଘ୍ର ବଦଳ କରିବା ପାଇଁ ଡାହାଣକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ଖୋଲିବା ପାଇଁ ପୁଣି ଟାପ୍ କରନ୍ତୁ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ଖୋଲିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ପୁଣି ଚେଷ୍ଟା କରିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ବ୍ୟବହାର କରିବାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ଏହି ଡିଭାଇସଟି ଆପଣଙ୍କ ସଂସ୍ଥାର ଅଟେ"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ଏହି ଡିଭାଇସଟି <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>ର ଅଟେ"</string>
<string name="phone_hint" msgid="6682125338461375925">"ଫୋନ୍ ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ଆପ୍ଲିକେସନ୍ଗୁଡିକ ଆପଣଙ୍କ <xliff:g id="TYPES_LIST">%s</xliff:g> ବ୍ୟବହାର କରୁଛନ୍ତି।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ଏବଂ "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ବ୍ୟବହାର କରୁଛି"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ଏବେ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ବ୍ୟବହାର କରିଛି"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ଏଣ୍ଟରପ୍ରାଇଜ୍)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ଫୋନକଲ୍"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ମାଧ୍ୟମରେ)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"କ୍ୟାମେରା"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ଲୋକେସନ୍"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"ମାଇକ୍ରୋଫୋନ୍"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index e7ffe49..d33455d 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -344,10 +344,8 @@
<string name="quick_settings_ime_label" msgid="3351174938144332051">"ਇਨਪੁੱਟ ਵਿਧੀ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ਟਿਕਾਣਾ"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਬੰਦ"</string>
- <!-- no translation found for quick_settings_camera_label (1367149596242401934) -->
- <skip />
- <!-- no translation found for quick_settings_mic_label (8245831073612564953) -->
- <skip />
+ <string name="quick_settings_camera_label" msgid="1367149596242401934">"ਕੈਮਰਾ ਬਲਾਕ ਕਰੋ"</string>
+ <string name="quick_settings_mic_label" msgid="8245831073612564953">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਮਿਊਟ ਕਰੋ"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"ਮੀਡੀਆ ਡੀਵਾਈਸ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ਸਿਰਫ਼ ਸੰਕਟਕਾਲੀਨ ਕਾਲਾਂ"</string>
@@ -418,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ਰੋਕੋ"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"ਜਾਰੀ ਰੱਖਣ ਲਈ, <b><xliff:g id="APP">%s</xliff:g></b> ਨੂੰ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਤੱਕ ਪਹੁੰਚ ਦੀ ਲੋੜ ਹੈ।"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"ਜਾਰੀ ਰੱਖਣ ਲਈ, <b><xliff:g id="APP">%s</xliff:g></b> ਨੂੰ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰਾ ਤੱਕ ਪਹੁੰਚ ਦੀ ਲੋੜ ਹੈ।"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"ਡੀਵਾਈਸ"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ਐਪਾਂ ਵਿਚਾਲੇ ਤੇਜ਼ੀ ਨਾਲ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਘਸੀਟੋ"</string>
@@ -444,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ਖੋਲ੍ਹਣ ਲਈ ਦੁਬਾਰਾ ਟੈਪ ਕਰੋ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ਖੋਲ੍ਹਣ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਉੱਤੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ਵਰਤਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string>
<string name="phone_hint" msgid="6682125338461375925">"ਫ਼ੋਨ ਲਈ ਪ੍ਰਤੀਕ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
@@ -972,6 +967,16 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ਐਪਲੀਕੇਸ਼ਨਾਂ ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ਅਤੇ "</string>
+ <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
+ <skip />
<string name="privacy_type_camera" msgid="7974051382167078332">"ਕੈਮਰਾ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ਟਿਕਾਣਾ"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 0275626..6440a65 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -420,10 +420,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Nagrywanie ekranu"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Rozpocznij"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zatrzymaj"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Aby kontynuować, musisz przyznać aplikacji „<xliff:g id="APP">%s</xliff:g>” dostęp do mikrofonu urządzenia."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Aby kontynuować, musisz przyznać aplikacji „<xliff:g id="APP">%s</xliff:g>” dostęp do aparatu urządzenia."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Urządzenie"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Przesuń w górę, by przełączyć aplikacje"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Szybko przeciągnij w prawo, by przełączyć aplikacje"</string>
@@ -446,8 +444,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Kliknij ponownie, by otworzyć"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Przesuń w górę, by otworzyć"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Przesuń w górę, by spróbować ponownie"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odblokuj, by użyć NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"To urządzenie należy do Twojej organizacji"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Właściciel tego urządzenia: <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Aby włączyć telefon, przesuń palcem od ikony"</string>
@@ -980,6 +977,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacje używają: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" i "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacja <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> używa: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacja <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> używała ostatnio: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(wersja firmowa)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Rozmowa telefoniczna"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(przez: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"aparat"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokalizacja"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index b629b64..40dc2de 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> usou <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recentemente"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada telefônica"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(pelo app <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"câmera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"localização"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microfone"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 43933a4..22241f8 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"As aplicações estão a utilizar o(a) <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"A app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está a utilizar a app <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>."</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Recentemente, a app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilizou a app <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>."</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada telefónica"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(através de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"câmara"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"localização"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microfone"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index b629b64..40dc2de 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -967,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" e "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> usou <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recentemente"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada telefônica"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(pelo app <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"câmera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"localização"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microfone"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 5197de3..f32453b9 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -418,10 +418,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Înregistrarea ecranului"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Începeți"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Opriți"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Pentru a continua, <b><xliff:g id="APP">%s</xliff:g></b> necesită acces la microfonul dispozitivului."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Pentru a continua, <b><xliff:g id="APP">%s</xliff:g></b> necesită acces la camera dispozitivului."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Dispozitiv"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Glisați în sus pentru a comuta între aplicații"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Glisați la dreapta pentru a comuta rapid între aplicații"</string>
@@ -444,8 +442,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Atingeți din nou pentru a deschide"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Glisați în sus pentru a deschide"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Glisați pentru a încerca din nou"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Deblocați pentru a folosi NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Dispozitivul aparține organizației dvs."</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Glisați dinspre telefon"</string>
@@ -975,6 +972,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicațiile folosesc <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" și "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> folosește <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> a folosit recent <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prin <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"cameră foto"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"locație"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"microfon"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 17221c9..40fb258 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -420,10 +420,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Запись экрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Начать"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Остановить"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Чтобы продолжить, предоставьте приложению <b><xliff:g id="APP">%s</xliff:g></b> доступ к микрофону устройства."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Чтобы продолжить, предоставьте приложению <b><xliff:g id="APP">%s</xliff:g></b> доступ к камере устройства."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Устройство"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Чтобы переключиться между приложениями, проведите по экрану вверх."</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Перетащите вправо, чтобы быстро переключиться между приложениями"</string>
@@ -446,8 +444,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Нажмите ещё раз, чтобы открыть"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Проведите вверх, чтобы открыть"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Чтобы повторить попытку, проведите вверх"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Чтобы использовать NFC, разблокируйте устройство."</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Это устройство принадлежит вашей организации"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Этим устройством владеет организация \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string>
<string name="phone_hint" msgid="6682125338461375925">"Телефон: проведите от значка"</string>
@@ -980,6 +977,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"В приложениях используется <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" и "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Приложение \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" использует другое (<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>)."</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Приложение \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" недавно использовало другое (<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>)."</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративная версия)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Приложение для звонков"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(через приложение \"<xliff:g id="ATTRIBUTION">%s</xliff:g>\")"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"местоположение"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"микрофон"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 20bc150..f3d0409 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"තිර පටිගත කිරීම"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ආරම්භ කරන්න"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"නතර කරන්න"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"දිගටම කර ගෙන යාමට, <b><xliff:g id="APP">%s</xliff:g></b> හට ඔබගේ උපාංගයෙහි මයික්රෆෝනයට ප්රවේශය අවශ්යයි."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"දිගටම කර ගෙන යාමට, <b><xliff:g id="APP">%s</xliff:g></b> හට ඔබගේ උපාංගයෙහි කැමරාවට ප්රවේශය අවශ්යයි."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"උපාංගය"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"යෙදුම් මාරු කිරීමට ස්වයිප් කරන්න"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ඉක්මනින් යෙදුම් මාරු කිරීමට දකුණට අදින්න"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"විවෘත කිරීමට නැවත තට්ටු කරන්න"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"විවෘත කිරීමට ස්වයිප් කරන්න"</string>
<string name="keyguard_retry" msgid="886802522584053523">"නැවත උත්සාහ කිරීමට ඉහළට ස්වයිප් කරන්න"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC භාවිත කිරීමට අගුලු හරින්න"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"මෙම උපාංගය ඔබේ සංවිධානයට අයිතිය"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"මෙම උපාංගය <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> සංවිධානයට අයිතිය"</string>
<string name="phone_hint" msgid="6682125338461375925">"දුරකථනය සඳහා නිරූපකය වෙතින් ස්වයිප් කරන්න"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"යෙදුම් ඔබේ <xliff:g id="TYPES_LIST">%s</xliff:g> භාවිත කරමින් සිටී."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" සහ "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> භාවිත කරමින් ඇත"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> මෑතකදී <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> භාවිත කළේය"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ව්යවසාය)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"දුරකථන ඇමතුම"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> හරහා)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"කැමරාව"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ස්ථානය"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"මයික්රෝෆෝනය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 4984f0f..7e3f3c0 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -420,10 +420,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Záznam obrazovky"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začať"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončiť"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Ak chcete pokračovať, <b><xliff:g id="APP">%s</xliff:g></b> požaduje prístup k mikrofónu zariadenia."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Ak chcete pokračovať, <b><xliff:g id="APP">%s</xliff:g></b> požaduje prístup k fotoaparátu zariadenia."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Zariadenie"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Potiahnutím nahor prepnete aplikácie"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Presunutím doprava rýchlo prepnete aplikácie"</string>
@@ -446,8 +444,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Upozornenie otvoríte opätovným klepnutím"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Otvorte potiahnutím prstom nahor"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Potiahnutím nahor to skúste znova"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ak chcete použiť NFC, odomknite"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Toto zariadenie patrí vašej organizácii"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Toto zariadení patrí organizácii <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefón otvoríte prejdením prstom od ikony"</string>
@@ -980,6 +977,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikácie používajú zoznam <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" a "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> používa aplikáciu <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikácia <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> použila nedávno aplikáciu <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(podniková verzia)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonický hovor"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prostredníctvom aplikácie <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparát"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"poloha"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofón"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index ffc6c66..b7ebea9 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -420,10 +420,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Snemanje zaslona"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začni"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ustavi"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Za nadaljevanje potrebuje aplikacija <b><xliff:g id="APP">%s</xliff:g></b> dostop do mikrofona v napravi."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Za nadaljevanje potrebuje aplikacija <b><xliff:g id="APP">%s</xliff:g></b> dostop do fotoaparata v napravi."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Naprava"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Za preklop aplikacij povlecite navzgor"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Povlecite v desno za hiter preklop med aplikacijami"</string>
@@ -446,8 +444,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Znova se dotaknite, da odprete"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Povlecite navzgor, da odprete"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Povlecite navzgor za vnovičen poskus"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odklenite napravo, če želite uporabljati vmesnik NFC."</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Ta naprava pripada vaši organizaciji"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Ta naprava pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Povlecite z ikone za telefon"</string>
@@ -980,6 +977,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije uporabljajo <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" in "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> uporablja: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> je nedavno uporabila: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(za podjetja)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski klici"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prek aplikacije <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparat"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokacijo"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 21ac1e0..fede55c 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Regjistrimi i ekranit"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Nis"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ndalo"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Për të vazhduar, <b><xliff:g id="APP">%s</xliff:g></b> ka nevojë të qaset në mikrofonin e pajisjes sate."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Për të vazhduar, <b><xliff:g id="APP">%s</xliff:g></b> ka nevojë të qaset në kamerën e pajisjes sate."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Pajisja"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Rrëshqit shpejt lart për të ndërruar aplikacionet"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Zvarrit djathtas për të ndërruar aplikacionet me shpejtësi"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Trokit përsëri për ta hapur"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Rrëshqit lart për ta hapur"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Rrëshqit lart për të provuar përsëri"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Shkyçe për të përdorur NFC-në"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Kjo pajisje i përket organizatës sate"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Rrëshqit për të hapur telefonin"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacionet po përdorin <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" dhe "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> po përdor <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ka përdorur <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> së fundi"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ndërmarrje)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(nëpërmjet <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamerën"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"vendndodhjen"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofonin"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index e154cc5..0d82246 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -418,10 +418,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Снимак екрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Почните"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зауставите"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"<b><xliff:g id="APP">%s</xliff:g></b> захтева приступ микрофону уређаја ради настављања."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"<b><xliff:g id="APP">%s</xliff:g></b> захтева приступ камери уређаја ради настављања."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Уређај"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Превуците нагоре да бисте мењали апликације"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Превуците удесно да бисте брзо променили апликације"</string>
@@ -444,8 +442,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Додирните поново да бисте отворили"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Превуците нагоре да бисте отворили"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Превуците нагоре да бисте пробали поново"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Откључајте да бисте користили NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Овај уређај припада организацији"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Овај уређај припада организацији <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Превуците од иконе за телефон"</string>
@@ -975,6 +972,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Апликације користе <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" и "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Апликација <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> користи: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Апликација <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> је недавно користила: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(за предузећа)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефонски позив"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(преко: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камеру"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"локацију"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"микрофон"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 66ae227..969bdcd 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Skärminspelning"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starta"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppa"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"<b><xliff:g id="APP">%s</xliff:g></b> behöver behörighet till enhetens mikrofon för att fortsätta."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"<b><xliff:g id="APP">%s</xliff:g></b> behöver behörighet till enhetens kamera för att fortsätta."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Enhet"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Byt appar genom att svepa uppåt"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Tryck och dra åt höger för att snabbt byta mellan appar"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tryck igen för att öppna"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Öppna genom att svepa uppåt"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Svep uppåt om du vill försöka igen"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Lås upp om du vill använda NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Den här enheten tillhör organisationen"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Den här enheten tillhör <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Svep från ikonen och öppna telefonen"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"<xliff:g id="TYPES_LIST">%s</xliff:g> används av appar."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" och "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> använder <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> använde <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> nyligen"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(företag)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonsamtal"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(genom <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"plats"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 26e6b8c..4055e8d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Rekodi ya Skrini"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Anza kurekodi"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Acha kurekodi"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Ili uendelee, <b><xliff:g id="APP">%s</xliff:g></b> inahitaji ruhusa ya kufikia maikrofoni ya kifaa chako."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Ili uendelee, <b><xliff:g id="APP">%s</xliff:g></b> inahitaji ruhusa ya kufikia kamera ya kifaa chako."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Kifaa"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Telezesha kidole juu ili ubadilishe programu"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Buruta kulia ili ubadilishe programu haraka"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Gusa tena ili ufungue"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Telezesha kidole juu ili ufungue"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Telezesha kidole juu ili ujaribu tena"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Fungua ili utumie NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Kifaa hiki kinamilikiwa na shirika lako"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Kifaa hiki kinamilikiwa na <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telezesha kidole kutoka kwa aikoni ili ufikie simu"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programu zinatumia <xliff:g id="TYPES_LIST">%s</xliff:g> yako."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" na "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> inatumia <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ilitumia <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> hivi majuzi"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(biashara)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Simu"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(kupitia <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"mahali"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"maikrofoni"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 132713b..cbac87e 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ஸ்கிரீன் ரெக்கார்டு"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"தொடங்கு"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"நிறுத்து"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"தொடர, உங்கள் சாதனத்தின் மைக்ரோஃபோனை அணுகுவதற்கு <b><xliff:g id="APP">%s</xliff:g></b> ஆப்ஸுக்கு அனுமதி வேண்டும்."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"தொடர, உங்கள் சாதனத்தின் கேமராவை அணுகுவதற்கு <b><xliff:g id="APP">%s</xliff:g></b> ஆப்ஸுக்கு அனுமதி வேண்டும்."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"சாதனம்"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ஆப்ஸிற்கு இடையே மாற்றுவதற்கு, மேல்நோக்கி ஸ்வைப் செய்க"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ஆப்ஸை வேகமாக மாற்ற, வலப்புறம் இழுக்கவும்"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"திறக்க, மீண்டும் தட்டவும்"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"திறப்பதற்கு மேல் நோக்கி ஸ்வைப் செய்யவும்"</string>
<string name="keyguard_retry" msgid="886802522584053523">"மீண்டும் முயல மேல்நோக்கி ஸ்வைப் செய்யவும்"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCயைப் பயன்படுத்த அன்லாக் செய்யவும்"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு சொந்தமானது"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"இந்த சாதனம் <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> நிறுவனத்துக்கு சொந்தமானது"</string>
<string name="phone_hint" msgid="6682125338461375925">"ஃபோனிற்கு ஐகானிலிருந்து ஸ்வைப் செய்யவும்"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"உங்கள் <xliff:g id="TYPES_LIST">%s</xliff:g> ஆகியவற்றை ஆப்ஸ் பயன்படுத்துகின்றன."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" மற்றும் "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ஆப்ஸ் <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> பயன்படுத்துகிறது"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"சமீபத்தில் <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ஆப்ஸ் <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> பயன்படுத்தியுள்ளது"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(நிறுவனப் பதிப்பு)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"மொபைல் அழைப்பு"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> மூலம்)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"கேமரா"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"இருப்பிடம்"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"மைக்ரோஃபோன்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 13758d9..45ef339 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"స్క్రీన్ రికార్డ్"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ప్రారంభించు"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ఆపు"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"కొనసాగించడానికి, <b><xliff:g id="APP">%s</xliff:g></b>కు మీ పరికరం యొక్క మైక్రోఫోన్ యాక్సెస్ అవసరం."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"కొనసాగించడానికి, <b><xliff:g id="APP">%s</xliff:g></b&gtకు మీ పరికరం యొక్క కెమెరా యాక్సెస్ అవసరం."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"పరికరం"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"యాప్లను మార్చడం కోసం ఎగువకు స్వైప్ చేయండి"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"యాప్లను శీఘ్రంగా స్విచ్ చేయడానికి కుడి వైపుకు లాగండి"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"తెరవడానికి మళ్లీ నొక్కండి"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"తెరవడానికి, పైకి స్వైప్ చేయండి"</string>
<string name="keyguard_retry" msgid="886802522584053523">"మళ్ళీ ప్రయత్నించడానికి పైకి స్వైప్ చేయండి"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCను ఉపయోగించడానికి అన్లాక్ చేయండి"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ఈ పరికరం మీ సంస్థకు చెందినది"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>కు చెందినది"</string>
<string name="phone_hint" msgid="6682125338461375925">"ఫోన్ కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string>
@@ -970,6 +967,16 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"అప్లికేషన్లు మీ <xliff:g id="TYPES_LIST">%s</xliff:g>ని ఉపయోగిస్తున్నాయి."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" మరియు "</string>
+ <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
+ <skip />
+ <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
+ <skip />
<string name="privacy_type_camera" msgid="7974051382167078332">"కెమెరా"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"లొకేషన్"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"మైక్రోఫోన్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 5c08293..8b197ac 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"บันทึกหน้าจอ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"เริ่ม"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"หยุด"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"<b><xliff:g id="APP">%s</xliff:g></b> ต้องได้รับสิทธิ์เข้าถึงไมโครโฟนของอุปกรณ์เพื่อดำเนินการต่อ"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"<b><xliff:g id="APP">%s</xliff:g></b> ต้องได้รับสิทธิ์เข้าถึงกล้องของอุปกรณ์เพื่อดำเนินการต่อ"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"อุปกรณ์"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"เลื่อนขึ้นเพื่อสลับแอป"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ลากไปทางขวาเพื่อสลับแอปอย่างรวดเร็ว"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"แตะอีกครั้งเพื่อเปิด"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"เลื่อนขึ้นเพื่อเปิด"</string>
<string name="keyguard_retry" msgid="886802522584053523">"เลื่อนขึ้นเพื่อลองอีกครั้ง"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"ปลดล็อกเพื่อใช้ NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g> เป็นเจ้าของอุปกรณ์นี้"</string>
<string name="phone_hint" msgid="6682125338461375925">"เลื่อนไอคอนโทรศัพท์"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"หลายแอปพลิเคชันใช้<xliff:g id="TYPES_LIST">%s</xliff:g>ของคุณอยู่"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" และ "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> กำลังใช้<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ใช้<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>เมื่อเร็วๆ นี้"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(องค์กร)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"โทรศัพท์"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(ผ่านทาง <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"กล้องถ่ายรูป"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ตำแหน่ง"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"ไมโครโฟน"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9f32232..a4a768b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Pag-record ng Screen"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Magsimula"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ihinto"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Para magpatuloy, kailangan ng <b><xliff:g id="APP">%s</xliff:g></b> ng access sa mikropono ng iyong device."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Para magpatuloy, kailangan ng <b><xliff:g id="APP">%s</xliff:g></b> ng access sa camera ng iyong device."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Mag-swipe pataas upang lumipat ng app"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"I-drag pakanan para mabilisang magpalipat-lipat ng app"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"I-tap ulit upang buksan"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Mag-swipe pataas para buksan"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Mag-swipe pataas para subukan ulit"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"I-unlock para magamit ang NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Pagmamay-ari ng iyong organisasyon ang device na ito"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Pagmamay-ari ng <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ang device na ito"</string>
<string name="phone_hint" msgid="6682125338461375925">"Mag-swipe mula sa icon para sa telepono"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ginagamit ng mga application ang iyong <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" at "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Ginagamit ng <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ang <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Kamakailang ginamit ng <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ang <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Tawag sa telepono"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(sa pamamagitan ng <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokasyon"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikropono"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 8c1016d..3040ea0 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekran Kaydı"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Başlat"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Durdur"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Devam etmek için <b><xliff:g id="APP">%s</xliff:g></b> uygulamasının cihazınızın mikrofonuna erişmesi gerekiyor."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Devam etmek için <b><xliff:g id="APP">%s</xliff:g></b> uygulamasının cihazınızın kamerasına erişmesi gerekiyor."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Cihaz"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Uygulamalar arasında geçiş yapmak için yukarı kaydırın"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Uygulamaları hızlıca değiştirmek için sağa kaydırın"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Açmak için tekrar dokunun"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Açmak için yukarı kaydırın"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Tekrar denemek için yukarı kaydırın"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC\'yi kullanmak için kilidi açın"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Bu cihaz, kuruluşunuza ait"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> adlı kuruluşa ait"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefon için, simgeden hızlıca kaydırın"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Uygulamalar şunları kullanıyor: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ve "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> uygulamasını kullanıyor"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, yakın zamanda <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> uygulamasını kullandı"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(kurumsal)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefon çağrısı"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> aracılığıyla)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"konum"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 45d4ad9..b6b7cee 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -420,10 +420,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Запис екрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Почати"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зупинити"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Щоб продовжити, надайте додатку <b><xliff:g id="APP">%s</xliff:g></b> доступ до мікрофона пристрою."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Щоб продовжити, надайте додатку <b><xliff:g id="APP">%s</xliff:g></b> доступ до камери пристрою."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Пристрій"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Проводьте пальцем угору, щоб переходити між додатками"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Перетягуйте праворуч, щоб швидко переходити між додатками"</string>
@@ -446,8 +444,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Торкніться знову, щоб відкрити"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Проведіть пальцем угору, щоб відкрити"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Проведіть пальцем угору, щоб повторити спробу"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Розблокуйте екран, щоб скористатись NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Цей пристрій належить вашій організації"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string>
<string name="phone_hint" msgid="6682125338461375925">"Телефон: проведіть пальцем від значка"</string>
@@ -980,6 +977,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Додатки використовують <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" і "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Додаток <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> використовує функцію \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Додаток <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> нещодавно використав функцію \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративний додаток)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Додаток для телефонних дзвінків"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(через <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камеру"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"місце"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"мікрофон"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 0216797..15c9607 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"اسکرین ریکارڈر کریں"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"آغاز"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"روکیں"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"جاری رکھنے کیلئے <b><xliff:g id="APP">%s</xliff:g></b> کو آپ کے آلے کے مائیکروفون تک رسائی درکار ہے۔"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"جاری رکھنے کیلئے <b><xliff:g id="APP">%s</xliff:g></b> کو آپ کے آلے کے کیمرے تک رسائی درکار ہے۔"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"آلہ"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ایپس سوئچ کرنے کیلئے اوپر سوائپ کریں"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"تیزی سے ایپس کو سوئچ کرنے کے لیے دائیں طرف گھسیٹیں"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"کھولنے کیلئے دوبارہ تھپتھپائیں"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"کھولنے کے لیے اوپر سوائپ کريں"</string>
<string name="keyguard_retry" msgid="886802522584053523">"دوبارہ کوشش کرنے کے لیے اوپر سوائپ کريں"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC استعمال کرنے کیلئے غیر مقفل کریں"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"یہ آلہ آپ کی تنظیم کا ہے"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"یہ آلہ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> کا ہے"</string>
<string name="phone_hint" msgid="6682125338461375925">"فون کیلئے آئیکن سے سوائپ کریں"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ایپلیکیشنز آپ کی <xliff:g id="TYPES_LIST">%s</xliff:g> کا استعمال کر رہی ہیں۔"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" اور "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> کا استعمال کر رہی ہے"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> نے حال ہی میں <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> کا استعمال کیا"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(انٹرپرائز)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"فون کال"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> کے ذریعے)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"کیمرا"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"مقام"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"مائیکروفون"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index e9bc22a..3044a98 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -345,7 +345,7 @@
<string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Joylashuvni aniqlash xizmati yoqilmagan"</string>
<string name="quick_settings_camera_label" msgid="1367149596242401934">"Kamerani bloklash"</string>
- <string name="quick_settings_mic_label" msgid="8245831073612564953">"Mikrofonni oʻchirish"</string>
+ <string name="quick_settings_mic_label" msgid="8245831073612564953">"Mikrofonni ovozsiz qilish"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media qurilma"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Favqulodda chaqiruvlar"</string>
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekranni yozib olish"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Boshlash"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Toʻxtatish"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Davom etish uchun <b> <xliff:g id="APP">%s</xliff:g></b> mikrofoningizdan foydalanishi kerak."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Davom etish uchun <b><xliff:g id="APP">%s</xliff:g></b> qurilmangiz kamerasiga kirishi kerak."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Qurilma"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Ilovalarni almashtirish uchun ekranni tepaga suring"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Ilovalarni tezkor almashtirish uchun o‘ngga torting"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ochish uchun yana bosing"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ochish uchun tepaga suring"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Qayta urinish uchun tepaga suring"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ishlatish uchun qurilma qulfini oching"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Bu qurilma tashkilotingizga tegishli"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Bu qurilma <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> tashkilotiga tegishli"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefonni ochish uchun suring"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ilovalarda ishlatilmoqda: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" va "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> hozir <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ishlatmoqda"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> yaqinda <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ishlatgan"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(korporativ)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefon chaqiruvi"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> orqali)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"joylashuv"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofon"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index e62a33b..4913be4 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ghi lại nội dung trên màn hình"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Bắt đầu"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dừng"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Để tiếp tục, <b><xliff:g id="APP">%s</xliff:g></b> cần quyền truy cập vào micrô trên thiết bị của bạn."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Để tiếp tục, <b><xliff:g id="APP">%s</xliff:g></b> cần quyền truy cập vào máy ảnh trên thiết bị của bạn."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Thiết bị"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Vuốt lên để chuyển đổi ứng dụng"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Kéo sang phải để chuyển đổi nhanh giữa các ứng dụng"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Nhấn lại để mở"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Vuốt lên để mở"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Vuốt lên để thử lại"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Mở khóa để sử dụng NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Thiết bị này thuộc về tổ chức của bạn"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Thiết bị này thuộc về <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Vuốt từ biểu tượng để mở điện thoại"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Các ứng dụng đang dùng <xliff:g id="TYPES_LIST">%s</xliff:g> của bạn."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" và "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> đang sử dụng <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Gần đây, <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> đã sử dụng <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(doanh nghiệp)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Gọi điện thoại"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(thông qua <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"máy ảnh"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"vị trí"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"micrô"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 050eea5..3479d00 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"屏幕录制"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"开始"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"如要继续操作,请向<b><xliff:g id="APP">%s</xliff:g></b>授予设备的麦克风使用权。"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"如要继续操作,请向<b><xliff:g id="APP">%s</xliff:g></b>授予设备的相机使用权。"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"设备"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"向上滑动可切换应用"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"向右拖动可快速切换应用"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"再次点按即可打开"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑动即可打开"</string>
<string name="keyguard_retry" msgid="886802522584053523">"向上滑动即可重试"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"需要解锁才能使用 NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"此设备归贵单位所有"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"此设备归<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>所有"</string>
<string name="phone_hint" msgid="6682125338461375925">"滑动图标即可拨打电话"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多个应用正在使用您的<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 和 "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>正在使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>最近曾使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(企业版)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"电话"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(通过<xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"相机"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"位置信息"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"麦克风"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 15709f7..314b728 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"畫面錄影"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"如要繼續,<b><xliff:g id="APP">%s</xliff:g></b> 需要裝置的麥克風存取權。"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"如要繼續,<b><xliff:g id="APP">%s</xliff:g></b> 需要裝置的相機存取權。"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"裝置"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"向上滑動即可切換應用程式"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"向右拖曳即可快速切換應用程式"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"再次輕按即可開啟"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string>
<string name="keyguard_retry" msgid="886802522584053523">"請向上滑動以再試一次"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"解鎖以使用 NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"此裝置屬於您的機構"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"此裝置屬於「<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>」"</string>
<string name="phone_hint" msgid="6682125338461375925">"從圖示滑動即可使用手機功能"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 和 "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」正在使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」最近曾使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(企業版本)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"電話"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(透過「<xliff:g id="ATTRIBUTION">%s</xliff:g>」)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"相機"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"位置"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"麥克風"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 90cf0b1..68b631f 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"螢幕畫面錄製"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"如要繼續操作,請將裝置的麥克風存取權授予「<xliff:g id="APP">%s</xliff:g>」<b></b>。"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"如要繼續操作,請將裝置的相機存取權授予「<xliff:g id="APP">%s</xliff:g>」<b></b>。"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"裝置"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"向上滑動即可切換應用程式"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"向右拖曳即可快速切換應用程式"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"再次輕觸即可開啟"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string>
<string name="keyguard_retry" msgid="886802522584053523">"向上滑動即可重試"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"如要使用 NFC,請先解鎖"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"這部裝置的擁有者為貴機構"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"這部裝置的擁有者為「<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>」"</string>
<string name="phone_hint" msgid="6682125338461375925">"滑動手機圖示即可啟用"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" 和 "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」正在使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」最近曾使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(企業版)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"電話"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(透過「<xliff:g id="ATTRIBUTION">%s</xliff:g>」)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"相機"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"位置"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"麥克風"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 3a77730..78c4f17 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -416,10 +416,8 @@
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Irekhodi lesikrini"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Qala"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Misa"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_dialog_content (8643239110815357707) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_dialog_content (7773612142162829116) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Ukuze uqhubeke, <b>i-<xliff:g id="APP">%s</xliff:g></b> idinga ukufinyelela imakrofoni yedivayisi yakho."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Ukuze uqhubeke, <b>i-<xliff:g id="APP">%s</xliff:g></b> idinga ukufinyelela ikhamera yakho."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Idivayisi"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swayiphela phezulu ukuze ushintshe izinhlelo zokusebenza"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Hudula ngqo ukuze ushintshe ngokushesha izinhlelo zokusebenza"</string>
@@ -442,8 +440,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Thepha futhi ukuze uvule"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swayiphela phezulu ukuze uvule"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Swayiphela phezulu ukuze uzame futhi"</string>
- <!-- no translation found for require_unlock_for_nfc (1305686454823018831) -->
- <skip />
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Vula ukuze usebenzise i-NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Le divayisi eyenhlangano yakho"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Le divayisi ngeye-<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Swayiphela ifoni kusukela kusithonjana"</string>
@@ -970,6 +967,11 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Izinhlelo zokusebenza zisebenzisa i-<xliff:g id="TYPES_LIST">%s</xliff:g> yakho."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" kanye "</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"I-<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> isebenzisa i-<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"I-<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> isebenzise i-<xliff:g id="APP_OPP_NAME">%2$s</xliff:g> kamuva nje"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ibhizinisi)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Ikholi yefoni"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(kuya ku-<xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ikhamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"indawo"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"imakrofoni"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 4059b49..e2fe223 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -171,5 +171,11 @@
<declare-styleable name="PagedTileLayout">
<attr name="sideLabels" format="boolean"/>
</declare-styleable>
+
+ <declare-styleable name="CropView">
+ <attr name="handleThickness" format="dimension" />
+ <attr name="handleColor" format="color" />
+ <attr name="scrimColor" format="color" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e510930..1fac96b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1181,6 +1181,12 @@
<dimen name="ongoing_appops_dialog_side_margins">@dimen/notification_shade_content_margin_horizontal</dimen>
+ <dimen name="ongoing_appops_dialog_circle_size">32dp</dimen>
+
+ <dimen name="ongoing_appops_dialog_icon_size">20dp</dimen>
+
+ <dimen name="ongoing_appops_dialog_side_padding">16dp</dimen>
+
<!-- Size of the RAT type for CellularTile -->
<dimen name="celltile_rat_type_size">10sp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 49f9109..01e54ff 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -31,4 +31,9 @@
<!-- AOD/Lockscreen alternate layout -->
<bool name="flag_keyguard_layout">false</bool>
+
+ <bool name="flag_brightness_slider">false</bool>
+
+ <!-- People Tile flag -->
+ <bool name="flag_conversations">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4baa06a..81b7a7b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1888,7 +1888,7 @@
<!-- Notification Inline controls: describes how the notification was adjusted [CHAR_LIMIT=NONE] -->
<string name="feedback_demoted">This notification was automatically <b>ranked lower</b> in your shade.</string>
<!-- Notification Inline controls: prompts the user for feedback [CHAR_LIMIT=NONE] -->
- <string name="feedback_prompt">Was this correct?</string>
+ <string name="feedback_prompt">Let the developer know your feedback. Was this correct?</string>
<!-- Notification Inline controls: responds to user provided feedback [CHAR_LIMIT=NONE] -->
<string name="feedback_response">Thanks for your feedback!</string>
<string name="feedback_ok">OK</string>
@@ -2556,8 +2556,8 @@
<!-- Text for privacy dialog, indicating that the application is the enterprise version [CHAR LIMIT=NONE] -->
<string name="ongoing_privacy_dialog_enterprise">(enterprise)</string>
- <!-- Text for privacy dialog, identifying the phonecall app [CHAR LIMIT=NONE]-->
- <string name="ongoing_privacy_dialog_phonecall">Phonecall</string>
+ <!-- Text for privacy dialog, identifying the phone call app [CHAR LIMIT=NONE]-->
+ <string name="ongoing_privacy_dialog_phonecall">Phone call</string>
<!-- Text for privacy dialog, indicating that an app is using an op on behalf of another [CHAR LIMIT=NONE] -->
<string name="ongoing_privacy_dialog_attribution_text">(through <xliff:g id="attribution" example="Special app">%s</xliff:g>)</string>
@@ -2580,9 +2580,6 @@
<!-- What to show on the ambient display player when song doesn't have a title. [CHAR LIMIT=20] -->
<string name="music_controls_no_title">No title</string>
- <!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
- <string name="restart_button_description">Tap to restart this app and go full screen.</string>
-
<!-- Action in accessibility menu to move the stack of bubbles [CHAR LIMIT=20] -->
<string name="bubble_accessibility_action_move">Move</string>
@@ -2771,13 +2768,6 @@
<!-- Controls menu, edit [CHAR_LIMIT=30] -->
<string name="controls_menu_edit">Edit controls</string>
- <!-- Device-specific path to the node for enabling/disabling the high-brightness mode -->
- <string name="udfps_hbm_sysfs_path" translatable="false"></string>
- <!-- Device-specific payload for enabling the high-brightness mode -->
- <string name="udfps_hbm_enable_command" translatable="false"></string>
- <!-- Device-specific payload for disabling the high-brightness mode -->
- <string name="udfps_hbm_disable_command" translatable="false"></string>
-
<!-- Title for the media output group dialog with media related devices [CHAR LIMIT=50] -->
<string name="media_output_dialog_add_output">Add outputs</string>
<!-- Title for the media output slice with group devices [CHAR LIMIT=50] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index db260ce..a74b564 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -354,15 +354,17 @@
</style>
<style name="LockPatternStyle">
- <item name="*android:regularColor">?android:attr/textColorPrimary</item>
+ <item name="*android:regularColor">?android:attr/colorAccent</item>
<item name="*android:successColor">?android:attr/textColorPrimary</item>
<item name="*android:errorColor">?android:attr/colorError</item>
+ <item name="*android:dotColor">?android:attr/textColorSecondary</item>
</style>
<style name="LockPatternStyleBiometricPrompt">
<item name="*android:regularColor">?android:attr/colorForeground</item>
<item name="*android:successColor">?android:attr/colorForeground</item>
<item name="*android:errorColor">?android:attr/colorError</item>
+ <item name="*android:dotColor">?android:attr/textColorSecondary</item>
</style>
<style name="qs_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
@@ -745,4 +747,9 @@
* Title: headline, medium 20sp
* Message: body, 16 sp -->
<style name="Theme.ControlsRequestDialog" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert"/>
+
+ <style name="TextAppearance.PrivacyDialog">
+ <item name="android:textSize">14sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index 10e28c4..d0c63a8 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -15,8 +15,10 @@
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="180dp"
+ android:minWidth="140dp"
android:minHeight="40dp"
+ android:minResizeWidth="110dp"
+ android:minResizeHeight="40dp"
android:updatePeriodMillis="60000"
android:previewImage="@drawable/ic_android"
android:resizeMode="horizontal|vertical"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 2a0715e..87f6b82 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -118,9 +118,9 @@
}
public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
- final RemoteAnimationTargetCompat[] appsCompat =
- new RemoteAnimationTargetCompat[apps != null ? apps.length : 0];
- for (int i = 0; i < apps.length; i++) {
+ final int length = apps != null ? apps.length : 0;
+ final RemoteAnimationTargetCompat[] appsCompat = new RemoteAnimationTargetCompat[length];
+ for (int i = 0; i < length; i++) {
appsCompat[i] = new RemoteAnimationTargetCompat(apps[i]);
}
return appsCompat;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index fbabaa4..70021b6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -114,13 +114,10 @@
for (int i = params.length - 1; i >= 0; i--) {
SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
params[i];
+ t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame);
surfaceParams.applyTo(t);
}
- if (mTargetViewRootImpl != null) {
- mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
- } else {
- t.apply();
- }
+ t.apply();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
.sendToTarget();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
index 89c60f1..4a28d56 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
@@ -56,12 +56,4 @@
});
}
}
-
- public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) {
- if (mViewRoot != null) {
- mViewRoot.mergeWithNextTransaction(t, frame);
- } else {
- t.apply();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index 62d3093..ca99563 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -38,7 +38,7 @@
* The time's text color is a gradient that changes its colors based on its controller.
*/
public class AnimatableClockView extends TextView {
- private static final CharSequence FORMAT_12_HOUR = "h\nmm";
+ private static final CharSequence FORMAT_12_HOUR = "hh\nmm";
private static final CharSequence FORMAT_24_HOUR = "HH\nmm";
private static final long ANIM_DURATION = 300;
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index b7d7498..707ee29 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -154,7 +154,7 @@
**/
public void reloadColors() {
int color = Utils.getColorAttrDefaultColor(getContext(),
- android.R.attr.textColorSecondary);
+ android.R.attr.textColorPrimaryInverse);
setTextColor(color);
setBackground(getContext()
.getDrawable(com.android.systemui.R.drawable.kg_emergency_button_background));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index a40dc7a..85e9ca0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.DEFAULT_DISPLAY_GROUP;
import android.app.Presentation;
import android.content.Context;
@@ -116,6 +117,14 @@
if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
return false;
}
+ if (mTmpDisplayInfo.displayGroupId != DEFAULT_DISPLAY_GROUP) {
+ if (DEBUG) {
+ Log.i(TAG,
+ "Do not show KeyguardPresentation on a non-default group display");
+ }
+ return false;
+ }
+
return true;
}
/**
@@ -130,8 +139,7 @@
final int displayId = display.getDisplayId();
Presentation presentation = mPresentations.get(displayId);
if (presentation == null) {
- final Presentation newPresentation = new KeyguardPresentation(mContext, display,
- mKeyguardStatusViewComponentFactory);
+ final Presentation newPresentation = createPresentation(display);
newPresentation.setOnDismissListener(dialog -> {
if (newPresentation.equals(mPresentations.get(displayId))) {
mPresentations.remove(displayId);
@@ -152,6 +160,10 @@
return false;
}
+ KeyguardPresentation createPresentation(Display display) {
+ return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory);
+ }
+
/**
* @param displayId The id of the display to hide the presentation off.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index cc59c39..c182fd1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -29,12 +29,17 @@
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.OrientationEventListener;
import android.view.VelocityTracker;
+import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimationControlListener;
@@ -55,6 +60,7 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.List;
@@ -99,6 +105,12 @@
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
+ private boolean mIsSecurityViewLeftAligned = true;
+ private boolean mOneHandedMode = false;
+ private SecurityMode mSecurityMode = SecurityMode.Invalid;
+ private ViewPropertyAnimator mRunningOneHandedAnimator;
+ private final OrientationEventListener mOrientationEventListener;
+
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -157,16 +169,20 @@
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+
void userActivity();
+
void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
- * @param strongAuth wheher the user has authenticated with strong authentication like
- * pattern, password or PIN but not by trust agents or fingerprint
+ * @param strongAuth wheher the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
void finish(boolean strongAuth, int targetUserId);
+
void reset();
+
void onCancelClicked();
}
@@ -224,11 +240,136 @@
super(context, attrs, defStyle);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
mViewConfiguration = ViewConfiguration.get(context);
+
+ mOrientationEventListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ updateLayoutForSecurityMode(mSecurityMode);
+ }
+ };
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
+ mSecurityMode = securityMode;
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
+
+ updateLayoutForSecurityMode(securityMode);
+ mOrientationEventListener.enable();
+ }
+
+ void updateLayoutForSecurityMode(SecurityMode securityMode) {
+ mSecurityMode = securityMode;
+ mOneHandedMode = canUseOneHandedBouncer();
+
+ if (mOneHandedMode) {
+ mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
+ }
+
+ updateSecurityViewGravity();
+ updateSecurityViewLocation(false);
+ }
+
+ /** Return whether the one-handed keyguard should be enabled. */
+ private boolean canUseOneHandedBouncer() {
+ // Is it enabled?
+ if (!getResources().getBoolean(
+ com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
+ return false;
+ }
+
+ if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
+ return false;
+ }
+
+ return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ }
+
+ /** Read whether the one-handed keyguard should be on the left/right from settings. */
+ private boolean isOneHandedKeyguardLeftAligned(Context context) {
+ try {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
+ == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+ } catch (Settings.SettingNotFoundException ex) {
+ return true;
+ }
+ }
+
+ private void updateSecurityViewGravity() {
+ View securityView = findKeyguardSecurityView();
+
+ if (securityView == null) {
+ return;
+ }
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
+
+ if (mOneHandedMode) {
+ lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ } else {
+ lp.gravity = Gravity.CENTER;
+ }
+
+ securityView.setLayoutParams(lp);
+ }
+
+ /**
+ * Moves the inner security view to the correct location (in one handed mode) with animation.
+ * This is triggered when the user taps on the side of the screen that is not currently occupied
+ * by the security view .
+ */
+ private void updateSecurityViewLocation(boolean animate) {
+ View securityView = findKeyguardSecurityView();
+
+ if (securityView == null) {
+ return;
+ }
+
+ if (!mOneHandedMode) {
+ securityView.setTranslationX(0);
+ return;
+ }
+
+ if (mRunningOneHandedAnimator != null) {
+ mRunningOneHandedAnimator.cancel();
+ mRunningOneHandedAnimator = null;
+ }
+
+ int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
+
+ if (animate) {
+ mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
+ mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningOneHandedAnimator = null;
+ }
+ });
+
+ mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mRunningOneHandedAnimator.start();
+ } else {
+ securityView.setTranslationX(targetTranslation);
+ }
+ }
+
+ @Nullable
+ private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+
+ if (isKeyguardSecurityView(child)) {
+ return (KeyguardSecurityViewFlipper) child;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isKeyguardSecurityView(View view) {
+ return view instanceof KeyguardSecurityViewFlipper;
}
public void onPause() {
@@ -237,6 +378,7 @@
mAlertDialog = null;
}
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
+ mOrientationEventListener.disable();
}
@Override
@@ -318,19 +460,44 @@
if (mSwipeListener != null) {
mSwipeListener.onSwipeUp();
}
+ } else {
+ if (!mIsDragging) {
+ handleTap(event);
+ }
}
}
return true;
}
+ private void handleTap(MotionEvent event) {
+ // If we're using a fullscreen security mode, skip
+ if (!mOneHandedMode) {
+ return;
+ }
+
+ // Did the tap hit the "other" side of the bouncer?
+ if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f))
+ || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) {
+ mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
+
+ Settings.Global.putInt(
+ mContext.getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
+ mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
+ : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
+
+ updateSecurityViewLocation(true);
+ }
+ }
+
void setSwipeListener(SwipeListener swipeListener) {
mSwipeListener = swipeListener;
}
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
- .setStartVelocity(startVelocity)
- .animateToFinalPosition(0);
+ .setStartVelocity(startVelocity)
+ .animateToFinalPosition(0);
}
public void startDisappearAnimation(SecurityMode securitySelection) {
@@ -395,7 +562,9 @@
}
private void beginJankInstrument(int cuj) {
- InteractionJankMonitor.getInstance().begin(mSecurityViewFlipper.getSecurityView(), cuj);
+ KeyguardInputView securityView = mSecurityViewFlipper.getSecurityView();
+ if (securityView == null) return;
+ InteractionJankMonitor.getInstance().begin(securityView, cuj);
}
private void endJankInstrument(int cuj) {
@@ -438,18 +607,17 @@
return insets.inset(0, 0, 0, inset);
}
-
private void showDialog(String title, String message) {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
}
mAlertDialog = new AlertDialog.Builder(mContext)
- .setTitle(title)
- .setMessage(message)
- .setCancelable(false)
- .setNeutralButton(R.string.ok, null)
- .create();
+ .setTitle(title)
+ .setMessage(message)
+ .setCancelable(false)
+ .setNeutralButton(R.string.ok, null)
+ .create();
if (!(mContext instanceof Activity)) {
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
@@ -487,6 +655,44 @@
}
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int maxHeight = 0;
+ int maxWidth = 0;
+ int childState = 0;
+
+ int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(widthMeasureSpec) / 2,
+ MeasureSpec.getMode(widthMeasureSpec));
+
+ for (int i = 0; i < getChildCount(); i++) {
+ final View view = getChildAt(i);
+ if (view.getVisibility() != GONE) {
+ if (mOneHandedMode && isKeyguardSecurityView(view)) {
+ measureChildWithMargins(view, halfWidthMeasureSpec, 0,
+ heightMeasureSpec, 0);
+ } else {
+ measureChildWithMargins(view, widthMeasureSpec, 0,
+ heightMeasureSpec, 0);
+ }
+ final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ maxWidth = Math.max(maxWidth,
+ view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ maxHeight = Math.max(maxHeight,
+ view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ childState = combineMeasuredStates(childState, view.getMeasuredState());
+ }
+ }
+
+ // Check against our minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 1a8d420..fdab8db 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -404,6 +404,7 @@
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
+ mView.updateLayoutForSecurityMode(securityMode);
}
mSecurityCallback.onSecurityModeChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index c77c867..631c248 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -92,4 +92,13 @@
throw new IllegalStateException("Unknown security quality:" + security);
}
}
+
+ /**
+ * Returns whether the given security view should be used in a "one handed" way. This can be
+ * used to change how the security view is drawn (e.g. take up less of the screen, and align to
+ * one side).
+ */
+ public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
+ return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 7773fe9..75ef4b32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -128,6 +128,8 @@
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
+ if (child.getVisibility() != View.VISIBLE) continue;
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9908e67..d9a1eb6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1909,12 +1909,14 @@
}
/**
- * Whether to show the lock icon on lock screen and bouncer. This depends on the enrolled
- * biometrics to the device.
+ * Whether to show the lock icon on lock screen and bouncer.
*/
- public boolean shouldShowLockIcon() {
- return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser())
- && !isUdfpsEnrolled();
+ public boolean canShowLockIcon() {
+ if (mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1) {
+ return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser())
+ && !isUdfpsEnrolled();
+ }
+ return true;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 5384ddf..7a52d27 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -16,284 +16,27 @@
package com.android.systemui;
-import android.app.ActivityClient;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.hardware.display.DisplayManager;
-import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.PopupWindow;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.CommandQueue;
-
-import java.lang.ref.WeakReference;
import javax.inject.Inject;
-/** Shows a restart-activity button when the foreground activity is in size compatibility mode. */
+/**
+ * Shows a restart-activity button when the foreground activity is in size compatibility mode.
+ *
+ * // TODO remove this class after cleanup all dependencies.
+ * @deprecated Use {@link com.android.wm.shell.sizecompatui.SizeCompatUIController}
+ */
+@Deprecated
@SysUISingleton
-public class SizeCompatModeActivityController extends SystemUI implements CommandQueue.Callbacks {
- private static final String TAG = "SizeCompatMode";
+public class SizeCompatModeActivityController extends SystemUI {
- /** The showing buttons by display id. */
- private final SparseArray<RestartActivityButton> mActiveButtons = new SparseArray<>(1);
- /** Avoid creating display context frequently for non-default display. */
- private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
- private final CommandQueue mCommandQueue;
-
- /** Only show once automatically in the process life. */
- private boolean mHasShownHint;
-
- @VisibleForTesting
@Inject
- SizeCompatModeActivityController(Context context, TaskStackChangeListeners listeners,
- CommandQueue commandQueue) {
+ SizeCompatModeActivityController(Context context) {
super(context);
- mCommandQueue = commandQueue;
- listeners.registerTaskStackListener(new TaskStackChangeListener() {
- @Override
- public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
- // Note the callback already runs on main thread.
- updateRestartButton(displayId, activityToken);
- }
- });
}
@Override
- public void start() {
- mCommandQueue.addCallback(this);
- }
-
- @Override
- public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) {
- RestartActivityButton button = mActiveButtons.get(displayId);
- if (button == null) {
- return;
- }
- boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
- int newVisibility = imeShown ? View.GONE : View.VISIBLE;
- // Hide the button when input method is showing.
- if (button.getVisibility() != newVisibility) {
- button.setVisibility(newVisibility);
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- mDisplayContextCache.remove(displayId);
- removeRestartButton(displayId);
- }
-
- private void removeRestartButton(int displayId) {
- RestartActivityButton button = mActiveButtons.get(displayId);
- if (button != null) {
- button.remove();
- mActiveButtons.remove(displayId);
- }
- }
-
- private void updateRestartButton(int displayId, IBinder activityToken) {
- if (activityToken == null) {
- // Null token means the current foreground activity is not in size compatibility mode.
- removeRestartButton(displayId);
- return;
- }
-
- RestartActivityButton restartButton = mActiveButtons.get(displayId);
- if (restartButton != null) {
- restartButton.updateLastTargetActivity(activityToken);
- return;
- }
-
- Context context = getOrCreateDisplayContext(displayId);
- if (context == null) {
- Log.i(TAG, "Cannot get context for display " + displayId);
- return;
- }
-
- restartButton = createRestartButton(context);
- restartButton.updateLastTargetActivity(activityToken);
- if (restartButton.show()) {
- mActiveButtons.append(displayId, restartButton);
- } else {
- onDisplayRemoved(displayId);
- }
- }
-
- @VisibleForTesting
- RestartActivityButton createRestartButton(Context context) {
- RestartActivityButton button = new RestartActivityButton(context, mHasShownHint);
- mHasShownHint = true;
- return button;
- }
-
- private Context getOrCreateDisplayContext(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- return mContext;
- }
- Context context = null;
- WeakReference<Context> ref = mDisplayContextCache.get(displayId);
- if (ref != null) {
- context = ref.get();
- }
- if (context == null) {
- Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
- if (display != null) {
- context = mContext.createDisplayContext(display);
- mDisplayContextCache.put(displayId, new WeakReference<Context>(context));
- }
- }
- return context;
- }
-
- @VisibleForTesting
- static class RestartActivityButton extends ImageButton implements View.OnClickListener,
- View.OnLongClickListener {
-
- final WindowManager.LayoutParams mWinParams;
- final boolean mShouldShowHint;
- IBinder mLastActivityToken;
-
- final int mPopupOffsetX;
- final int mPopupOffsetY;
- PopupWindow mShowingHint;
-
- RestartActivityButton(Context context, boolean hasShownHint) {
- super(context);
- mShouldShowHint = !hasShownHint;
- Drawable drawable = context.getDrawable(R.drawable.btn_restart);
- setImageDrawable(drawable);
- setContentDescription(context.getString(R.string.restart_button_description));
-
- int drawableW = drawable.getIntrinsicWidth();
- int drawableH = drawable.getIntrinsicHeight();
- mPopupOffsetX = drawableW / 2;
- mPopupOffsetY = drawableH * 2;
-
- ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
- GradientDrawable mask = new GradientDrawable();
- mask.setShape(GradientDrawable.OVAL);
- mask.setColor(color);
- setBackground(new RippleDrawable(color, null /* content */, mask));
- setOnClickListener(this);
- setOnLongClickListener(this);
-
- mWinParams = new WindowManager.LayoutParams();
- mWinParams.gravity = getGravity(getResources().getConfiguration().getLayoutDirection());
- mWinParams.width = drawableW * 2;
- mWinParams.height = drawableH * 2;
- mWinParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
- mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- mWinParams.format = PixelFormat.TRANSLUCENT;
- mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- mWinParams.setTitle(SizeCompatModeActivityController.class.getSimpleName()
- + context.getDisplayId());
- }
-
- void updateLastTargetActivity(IBinder activityToken) {
- mLastActivityToken = activityToken;
- }
-
- /** @return {@code false} if the target display is invalid. */
- boolean show() {
- try {
- getContext().getSystemService(WindowManager.class).addView(this, mWinParams);
- } catch (WindowManager.InvalidDisplayException e) {
- // The target display may have been removed when the callback has just arrived.
- Log.w(TAG, "Cannot show on display " + getContext().getDisplayId(), e);
- return false;
- }
- return true;
- }
-
- void remove() {
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- }
- getContext().getSystemService(WindowManager.class).removeViewImmediate(this);
- }
-
- @Override
- public void onClick(View v) {
- ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken);
- }
-
- @Override
- public boolean onLongClick(View v) {
- showHint();
- return true;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mShouldShowHint) {
- showHint();
- }
- }
-
- @Override
- public void setLayoutDirection(int layoutDirection) {
- int gravity = getGravity(layoutDirection);
- if (mWinParams.gravity != gravity) {
- mWinParams.gravity = gravity;
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- showHint();
- }
- getContext().getSystemService(WindowManager.class).updateViewLayout(this,
- mWinParams);
- }
- super.setLayoutDirection(layoutDirection);
- }
-
- void showHint() {
- if (mShowingHint != null) {
- return;
- }
-
- View popupView = LayoutInflater.from(getContext()).inflate(
- R.layout.size_compat_mode_hint, null /* root */);
- 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;
-
- Button gotItButton = popupView.findViewById(R.id.got_it);
- gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
- null /* content */, null /* mask */));
- gotItButton.setOnClickListener(view -> popupWindow.dismiss());
- popupWindow.showAtLocation(this, mWinParams.gravity, mPopupOffsetX, mPopupOffsetY);
- }
-
- private static int getGravity(int layoutDirection) {
- return Gravity.BOTTOM
- | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END);
- }
- }
+ public void start() { }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 036fcf3..78f7966 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -127,7 +127,7 @@
}
// If SHOW_PEOPLE_SPACE is true, enable People Space widget provider.
- // TODO(b/170396074): Remove this when we don't need a widget anymore.
+ // TODO(b/170396074): Migrate to new feature flag (go/silk-flags-howto)
try {
int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.SHOW_PEOPLE_SPACE, 1);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
new file mode 100644
index 0000000..2083f2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
@@ -0,0 +1,41 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.NonNull;
+import android.view.Surface;
+
+/**
+ * Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
+ * enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
+ * illumination is no longer necessary.
+ */
+public interface HbmCallback {
+ /**
+ * UdfpsView will call this to enable the HBM before drawing the illumination dot.
+ *
+ * @param surface A valid surface for which the HBM should be enabled.
+ */
+ void enableHbm(@NonNull Surface surface);
+
+ /**
+ * UdfpsView will call this to disable the HBM when the illumination is not longer needed.
+ *
+ * @param surface A valid surface for which the HBM should be disabled.
+ */
+ void disableHbm(@NonNull Surface surface);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
index 40fe7b1..3ea8140 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
@@ -52,4 +52,18 @@
public void setAlpha(int alpha) {
mFingerprintDrawable.setAlpha(alpha);
}
+
+ /**
+ * @return The amount of padding that's needed on each side of the sensor, in pixels.
+ */
+ public int getPaddingX() {
+ return 0;
+ }
+
+ /**
+ * @return The amount of padding that's needed on each side of the sensor, in pixels.
+ */
+ public int getPaddingY() {
+ return 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index 52662ae..e07c8403 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -28,7 +28,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -81,15 +80,19 @@
}
@Override
+ public int getPaddingX() {
+ return (int) Math.ceil(SHADOW_RADIUS);
+ }
+
+ @Override
+ public int getPaddingY() {
+ return (int) Math.ceil(SHADOW_RADIUS);
+ }
+
+ @Override
public void setAlpha(int alpha) {
super.setAlpha(alpha);
-
- // Gradually fade into the notification shade color. This needs to be done because the
- // UDFPS view is drawn on a layer on top of the notification shade
- final float percent = alpha / 255.f;
- mSensorPaint.setColor(ColorUtils.blendARGB(mNotificationShadeColor, Color.WHITE, percent));
- mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0,
- ColorUtils.blendARGB(mNotificationShadeColor, Color.BLACK, percent));
+ mSensorPaint.setAlpha(alpha);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
new file mode 100644
index 0000000..4e3419e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -0,0 +1,131 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.systemui.doze.DozeReceiver;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations (such as enroll, keyguard, BiometricPrompt,
+ * FingerprintManager).
+ */
+public class UdfpsAnimationView extends View implements DozeReceiver,
+ StatusBar.ExpansionChangedListener {
+
+ private static final String TAG = "UdfpsAnimationView";
+
+ @NonNull private UdfpsView mParent;
+ @Nullable private UdfpsAnimation mUdfpsAnimation;
+ @NonNull private RectF mSensorRect;
+ private int mAlpha;
+
+ public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mSensorRect = new RectF();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (mUdfpsAnimation != null) {
+ final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255;
+ mUdfpsAnimation.setAlpha(alpha);
+ mUdfpsAnimation.draw(canvas);
+ }
+ }
+
+ private int expansionToAlpha(float expansion) {
+ // Fade to 0 opacity when reaching this expansion amount
+ final float maxExpansion = 0.4f;
+
+ if (expansion >= maxExpansion) {
+ return 0; // transparent
+ }
+
+ final float percent = expansion / maxExpansion;
+ return (int) ((1 - percent) * 255);
+ }
+
+ void setParent(@NonNull UdfpsView parent) {
+ mParent = parent;
+ }
+
+ void setAnimation(@Nullable UdfpsAnimation animation) {
+ mUdfpsAnimation = animation;
+ }
+
+ void onSensorRectUpdated(@NonNull RectF sensorRect) {
+ mSensorRect = sensorRect;
+ if (mUdfpsAnimation != null) {
+ mUdfpsAnimation.onSensorRectUpdated(mSensorRect);
+ }
+ }
+
+ void updateColor() {
+ if (mUdfpsAnimation != null) {
+ mUdfpsAnimation.updateColor();
+ }
+ }
+
+ @Override
+ public void dozeTimeTick() {
+ if (mUdfpsAnimation instanceof DozeReceiver) {
+ ((DozeReceiver) mUdfpsAnimation).dozeTimeTick();
+ }
+ }
+
+ @Override
+ public void onExpansionChanged(float expansion, boolean expanded) {
+ mAlpha = expansionToAlpha(expansion);
+ postInvalidate();
+ }
+
+ void onEnrollmentProgress(int remaining) {
+ if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
+ ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining);
+ }
+ }
+
+ void onEnrollmentHelp() {
+ if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
+ ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
+ }
+ }
+
+ public int getPaddingX() {
+ if (mUdfpsAnimation == null) {
+ return 0;
+ }
+ return mUdfpsAnimation.getPaddingX();
+ }
+
+ public int getPaddingY() {
+ if (mUdfpsAnimation == null) {
+ return 0;
+ }
+ return mUdfpsAnimation.getPaddingY();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index b373cff..baa5973 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -22,29 +22,22 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.RectF;
-import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.PowerManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
import android.util.Log;
-import android.util.MathUtils;
-import android.util.Spline;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.Surface;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -52,11 +45,8 @@
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;
-import com.android.systemui.util.settings.SystemSettings;
-
-import java.io.FileWriter;
-import java.io.IOException;
import javax.inject.Inject;
@@ -73,16 +63,13 @@
*/
@SuppressWarnings("deprecation")
@SysUISingleton
-public class UdfpsController implements DozeReceiver {
+public class UdfpsController implements DozeReceiver, HbmCallback {
private static final String TAG = "UdfpsController";
- // Gamma approximation for the sRGB color space.
- private static final float DISPLAY_GAMMA = 2.2f;
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
private final Context mContext;
private final FingerprintManager mFingerprintManager;
private final WindowManager mWindowManager;
- private final SystemSettings mSystemSettings;
private final DelayableExecutor mFgExecutor;
private final StatusBarStateController mStatusBarStateController;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
@@ -90,23 +77,6 @@
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
private final WindowManager.LayoutParams mCoreLayoutParams;
private final UdfpsView mView;
- // Debugfs path to control the high-brightness mode.
- private final String mHbmPath;
- private final String mHbmEnableCommand;
- private final String mHbmDisableCommand;
- private final boolean mHbmSupported;
- // Brightness in nits in the high-brightness mode.
- private final float mMaxNits;
- // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to a
- // brightness in nits.
- private final Spline mBacklightToNitsSpline;
- // A spline mapping from a value in nits to a backlight value of a hypothetical panel whose
- // maximum backlight value corresponds to our panel's high-brightness mode.
- // The output is normalized to the range [0, 1.0].
- private Spline mNitsToHbmBacklightSpline;
- // Default non-HBM backlight value normalized to the range [0, 1.0]. Used as a fallback when the
- // actual brightness value cannot be retrieved.
- private final float mDefaultBrightness;
// Indicates whether the overlay is currently showing. Even if it has been requested, it might
// not be showing.
private boolean mIsOverlayShowing;
@@ -152,7 +122,7 @@
@SuppressLint("ClickableViewAccessibility")
private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> {
UdfpsView view = (UdfpsView) v;
- final boolean isFingerDown = view.isShowScrimAndDot();
+ final boolean isFingerDown = view.isIlluminationRequested();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
@@ -183,18 +153,15 @@
@Main Resources resources,
LayoutInflater inflater,
@Nullable FingerprintManager fingerprintManager,
- DisplayManager displayManager,
WindowManager windowManager,
- SystemSettings systemSettings,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
- @NonNull ScrimController scrimController) {
+ @Nullable StatusBar statusBar) {
mContext = context;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
// fingerprint manager should never be null.
mFingerprintManager = checkNotNull(fingerprintManager);
mWindowManager = windowManager;
- mSystemSettings = systemSettings;
mFgExecutor = fgExecutor;
mStatusBarStateController = statusBarStateController;
@@ -211,72 +178,18 @@
PixelFormat.TRANSLUCENT);
mCoreLayoutParams.setTitle(TAG);
mCoreLayoutParams.setFitInsetsTypes(0);
+ mCoreLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
mCoreLayoutParams.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false);
mView.setSensorProperties(mSensorProps);
+ mView.setHbmCallback(this);
- mHbmPath = resources.getString(R.string.udfps_hbm_sysfs_path);
- mHbmEnableCommand = resources.getString(R.string.udfps_hbm_enable_command);
- mHbmDisableCommand = resources.getString(R.string.udfps_hbm_disable_command);
-
- mHbmSupported = !TextUtils.isEmpty(mHbmPath);
- mView.setHbmSupported(mHbmSupported);
- scrimController.addScrimChangedListener(mView);
+ statusBar.addExpansionChangedListener(mView);
statusBarStateController.addCallback(mView);
- // This range only consists of the minimum and maximum values, which only cover
- // non-high-brightness mode.
- float[] nitsRange = toFloatArray(resources.obtainTypedArray(
- com.android.internal.R.array.config_screenBrightnessNits));
- if (nitsRange.length < 2) {
- throw new IllegalArgumentException(
- String.format("nitsRange.length: %d. Must be >= 2", nitsRange.length));
- }
-
- // The last value of this range corresponds to the high-brightness mode.
- float[] nitsAutoBrightnessValues = toFloatArray(resources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
- if (nitsAutoBrightnessValues.length < 2) {
- throw new IllegalArgumentException(
- String.format("nitsAutoBrightnessValues.length: %d. Must be >= 2",
- nitsAutoBrightnessValues.length));
- }
-
- mMaxNits = nitsAutoBrightnessValues[nitsAutoBrightnessValues.length - 1];
- float[] hbmNitsRange = nitsRange.clone();
- hbmNitsRange[hbmNitsRange.length - 1] = mMaxNits;
-
- // This range only consists of the minimum and maximum backlight values, which only apply
- // in non-high-brightness mode.
- float[] normalizedBacklightRange = normalizeBacklightRange(
- resources.getIntArray(
- com.android.internal.R.array.config_screenBrightnessBacklight));
- if (normalizedBacklightRange.length < 2) {
- throw new IllegalArgumentException(
- String.format("normalizedBacklightRange.length: %d. Must be >= 2",
- normalizedBacklightRange.length));
- }
- if (normalizedBacklightRange.length != nitsRange.length) {
- throw new IllegalArgumentException(
- "normalizedBacklightRange.length != nitsRange.length");
- }
-
- mBacklightToNitsSpline = Spline.createSpline(normalizedBacklightRange, nitsRange);
- mNitsToHbmBacklightSpline = Spline.createSpline(hbmNitsRange, normalizedBacklightRange);
- mDefaultBrightness = obtainDefaultBrightness(mContext);
-
- // TODO(b/160025856): move to the "dump" method.
- Log.v(TAG, String.format("ctor | mNitsRange: [%f, %f]", nitsRange[0],
- nitsRange[nitsRange.length - 1]));
- Log.v(TAG, String.format("ctor | mHbmNitsRange: [%f, %f]", hbmNitsRange[0],
- hbmNitsRange[hbmNitsRange.length - 1]));
- Log.v(TAG, String.format("ctor | mNormalizedBacklightRange: [%f, %f]",
- normalizedBacklightRange[0],
- normalizedBacklightRange[normalizedBacklightRange.length - 1]));
-
mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
mIsOverlayShowing = false;
}
@@ -330,14 +243,37 @@
}
}
- private WindowManager.LayoutParams computeLayoutParams() {
+ private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimation animation) {
+ final int paddingX = animation != null ? animation.getPaddingX() : 0;
+ final int paddingY = animation != null ? animation.getPaddingY() : 0;
+
+ // Default dimensions assume portrait mode.
+ mCoreLayoutParams.x = mSensorProps.sensorLocationX - mSensorProps.sensorRadius - paddingX;
+ mCoreLayoutParams.y = mSensorProps.sensorLocationY - mSensorProps.sensorRadius - paddingY;
+ mCoreLayoutParams.height = 2 * mSensorProps.sensorRadius + 2 * paddingX;
+ mCoreLayoutParams.width = 2 * mSensorProps.sensorRadius + 2 * paddingY;
+
Point p = new Point();
// Gets the size based on the current rotation of the display.
mContext.getDisplay().getRealSize(p);
- mCoreLayoutParams.width = p.x;
- mCoreLayoutParams.x = p.x;
- mCoreLayoutParams.height = p.y;
- mCoreLayoutParams.y = p.y;
+
+ // Transform dimensions if the device is in landscape mode.
+ switch (mContext.getDisplay().getRotation()) {
+ case Surface.ROTATION_90:
+ mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
+ mCoreLayoutParams.y =
+ p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+ break;
+
+ case Surface.ROTATION_270:
+ mCoreLayoutParams.x =
+ p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
+ mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+ break;
+
+ default:
+ // Do nothing to stay in portrait mode.
+ }
return mCoreLayoutParams;
}
@@ -357,8 +293,9 @@
if (!mIsOverlayShowing) {
try {
Log.v(TAG, "showUdfpsOverlay | adding window");
- mView.setUdfpsAnimation(getUdfpsAnimationForReason(reason));
- mWindowManager.addView(mView, computeLayoutParams());
+ final UdfpsAnimation animation = getUdfpsAnimationForReason(reason);
+ mView.setUdfpsAnimation(animation);
+ mWindowManager.addView(mView, computeLayoutParams(animation));
mView.setOnTouchListener(mOnTouchListener);
mIsOverlayShowing = true;
} catch (RuntimeException e) {
@@ -402,36 +339,10 @@
});
}
- // Returns a value in the range of [0, 255].
- private int computeScrimOpacity() {
- // Backlight setting can be NaN, -1.0f, and [0.0f, 1.0f].
- float backlightSetting = mSystemSettings.getFloatForUser(
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBrightness,
- UserHandle.USER_CURRENT);
-
- // Constrain the backlight setting to [0.0f, 1.0f].
- float backlightValue = MathUtils.constrain(backlightSetting,
- PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX);
-
- // Interpolate the backlight value to nits.
- float nits = mBacklightToNitsSpline.interpolate(backlightValue);
-
- // Interpolate nits to a backlight value for a panel with enabled HBM.
- float interpolatedHbmBacklightValue = mNitsToHbmBacklightSpline.interpolate(nits);
-
- float gammaCorrectedHbmBacklightValue = (float) Math.pow(interpolatedHbmBacklightValue,
- 1.0f / DISPLAY_GAMMA);
- float scrimOpacity = PowerManager.BRIGHTNESS_MAX - gammaCorrectedHbmBacklightValue;
-
- // Interpolate the opacity value from [0.0f, 1.0f] to [0, 255].
- return BrightnessSynchronizer.brightnessFloatToInt(scrimOpacity);
- }
-
/**
* Request fingerprint scan.
*
- * This is intented to be called in response to a sensor that triggers an AOD interrupt for the
+ * This is intended to be called in response to a sensor that triggers an AOD interrupt for the
* fingerprint sensor.
*/
void onAodInterrupt(int screenX, int screenY, float major, float minor) {
@@ -451,7 +362,7 @@
/**
* Cancel fingerprint scan.
*
- * This is intented to be called after the fingerprint scan triggered by the AOD interrupt
+ * This is intended to be called after the fingerprint scan triggered by the AOD interrupt
* either succeeds or fails.
*/
void onCancelAodInterrupt() {
@@ -466,65 +377,27 @@
onFingerUp();
}
- protected void onFingerDown(int x, int y, float minor, float major) {
- if (mHbmSupported) {
- try {
- FileWriter fw = new FileWriter(mHbmPath);
- fw.write(mHbmEnableCommand);
- fw.close();
- } catch (IOException e) {
- mView.hideScrimAndDot();
- Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
- }
- }
- mView.setScrimAlpha(computeScrimOpacity());
- mView.setRunAfterShowingScrimAndDot(() -> {
- mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major);
- });
- mView.showScrimAndDot();
+ // This method can be called from the UI thread.
+ private void onFingerDown(int x, int y, float minor, float major) {
+ mView.startIllumination(() ->
+ mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major));
}
- protected void onFingerUp() {
+ // This method can be called from the UI thread.
+ private void onFingerUp() {
mFingerprintManager.onPointerUp(mSensorProps.sensorId);
- // Hiding the scrim before disabling HBM results in less noticeable flicker.
- mView.hideScrimAndDot();
- if (mHbmSupported) {
- try {
- FileWriter fw = new FileWriter(mHbmPath);
- fw.write(mHbmDisableCommand);
- fw.close();
- } catch (IOException e) {
- mView.showScrimAndDot();
- Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage());
- }
- }
+ mView.stopIllumination();
}
- private static float obtainDefaultBrightness(Context context) {
- return MathUtils.constrain(context.getDisplay().getBrightnessDefault(),
- PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+ @Override
+ public void enableHbm(@NonNull Surface surface) {
+ // Do nothing. This method can be implemented for devices that require the high-brightness
+ // mode for fingerprint illumination.
}
- private static float[] toFloatArray(TypedArray array) {
- final int n = array.length();
- float[] vals = new float[n];
- for (int i = 0; i < n; i++) {
- vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT);
- }
- array.recycle();
- return vals;
- }
-
- private static float[] normalizeBacklightRange(int[] backlight) {
- final int n = backlight.length;
- float[] normalizedBacklight = new float[n];
- for (int i = 0; i < n; i++) {
- normalizedBacklight[i] = BrightnessSynchronizer.brightnessIntToFloat(backlight[i]);
- }
- return normalizedBacklight;
- }
-
- protected UdfpsView getView() {
- return mView;
+ @Override
+ public void disableHbm(@NonNull Surface surface) {
+ // Do nothing. This method can be implemented for devices that require the high-brightness
+ // mode for fingerprint illumination.
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
new file mode 100644
index 0000000..8bea05b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
@@ -0,0 +1,41 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface that should be implemented by UI's that need to coordinate user touches,
+ * views/animations, and modules that start/stop display illumination.
+ */
+interface UdfpsIlluminator {
+ /**
+ * @param callback Invoked when HBM should be enabled or disabled.
+ */
+ void setHbmCallback(@Nullable HbmCallback callback);
+
+ /**
+ * Invoked when illumination should start.
+ * @param onIlluminatedRunnable Invoked when the display has been illuminated.
+ */
+ void startIllumination(@Nullable Runnable onIlluminatedRunnable);
+
+ /**
+ * Invoked when illumination should end.
+ */
+ void stopIllumination();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
new file mode 100644
index 0000000..97c215e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -0,0 +1,115 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * Under-display fingerprint sensor Surface View. The surface should be used for HBM-specific things
+ * only. All other animations should be done on the other view.
+ */
+public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
+ private static final String TAG = "UdfpsSurfaceView";
+
+ /**
+ * This is used instead of {@link android.graphics.drawable.Drawable}, because the latter has
+ * several abstract methods that are not used here but require implementation.
+ */
+ private interface SimpleDrawable {
+ void draw(Canvas canvas);
+ }
+
+ @NonNull private final SurfaceHolder mHolder;
+ @NonNull private final Paint mSensorPaint;
+ @NonNull private final SimpleDrawable mIlluminationDotDrawable;
+
+ @NonNull private RectF mSensorRect;
+ @Nullable private HbmCallback mHbmCallback;
+
+ public UdfpsSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mHolder = getHolder();
+ mHolder.setFormat(PixelFormat.RGBA_8888);
+
+ mSensorRect = new RectF();
+ mSensorPaint = new Paint(0 /* flags */);
+ mSensorPaint.setAntiAlias(true);
+ mSensorPaint.setARGB(255, 255, 255, 255);
+ mSensorPaint.setStyle(Paint.Style.FILL);
+
+ mIlluminationDotDrawable = canvas -> canvas.drawOval(mSensorRect, mSensorPaint);
+ }
+
+ @Override
+ public void setHbmCallback(@Nullable HbmCallback callback) {
+ mHbmCallback = callback;
+ }
+
+ @Override
+ public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
+ if (mHbmCallback != null && mHolder.getSurface().isValid()) {
+ mHbmCallback.enableHbm(mHolder.getSurface());
+ }
+ drawImmediately(mIlluminationDotDrawable);
+
+ if (onIlluminatedRunnable != null) {
+ // No framework API can reliably tell when a frame reaches the panel. A timeout is the
+ // safest solution. The frame should be displayed within 3 refresh cycles, which on a
+ // 60 Hz panel equates to 50 milliseconds.
+ postDelayed(onIlluminatedRunnable, 50 /* delayMillis */);
+ }
+ }
+
+ @Override
+ public void stopIllumination() {
+ if (mHbmCallback != null && mHolder.getSurface().isValid()) {
+ mHbmCallback.disableHbm(mHolder.getSurface());
+ }
+
+ invalidate();
+ }
+
+ void onSensorRectUpdated(@NonNull RectF sensorRect) {
+ mSensorRect = sensorRect;
+ }
+
+ /**
+ * Immediately draws the provided drawable on this SurfaceView's surface.
+ */
+ private void drawImmediately(@NonNull SimpleDrawable drawable) {
+ Canvas canvas = null;
+ try {
+ canvas = mHolder.lockCanvas();
+ drawable.draw(canvas);
+ } finally {
+ // Make sure the surface is never left in a bad state.
+ if (canvas != null) {
+ mHolder.unlockCanvasAndPost(canvas);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 96ecc7b..7e378d3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -27,88 +27,43 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.ViewTreeObserver;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
import com.android.systemui.R;
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;
/**
- * A full screen view with a configurable illumination dot and scrim.
+ * A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other
+ * animations.
*/
-public class UdfpsView extends SurfaceView implements DozeReceiver,
- StatusBarStateController.StateListener, ScrimController.ScrimChangedListener {
+public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator,
+ StatusBarStateController.StateListener, StatusBar.ExpansionChangedListener {
private static final String TAG = "UdfpsView";
- // Values in pixels.
- private static final float SENSOR_SHADOW_RADIUS = 2.0f;
-
private static final int DEBUG_TEXT_SIZE_PX = 32;
- @NonNull private final Rect mScrimRect;
- @NonNull private final Paint mScrimPaint;
- @NonNull private final Paint mDebugTextPaint;
-
+ @NonNull private final UdfpsSurfaceView mHbmSurfaceView;
+ @NonNull private final UdfpsAnimationView mAnimationView;
@NonNull private final RectF mSensorRect;
- @NonNull private final Paint mSensorPaint;
- private final float mSensorTouchAreaCoefficient;
-
- // Stores rounded up values from mSensorRect. Necessary for APIs that only take Rect (not RecF).
- @NonNull private final Rect mTouchableRegion;
- // mInsetsListener is used to set the touchable region for our window. Our window covers the
- // whole screen, and by default its touchable region is the whole screen. We use
- // mInsetsListener to restrict the touchable region and allow the touches outside of the sensor
- // to propagate to the rest of the UI.
- @NonNull private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener;
- @Nullable private UdfpsAnimation mUdfpsAnimation;
+ @NonNull private final Paint mDebugTextPaint;
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
- private boolean mShowScrimAndDot;
- private boolean mIsHbmSupported;
+ private final float mSensorTouchAreaCoefficient;
@Nullable private String mDebugMessage;
+ private boolean mIlluminationRequested;
private int mStatusBarState;
private boolean mNotificationShadeExpanded;
- private int mNotificationPanelAlpha;
-
- // Runnable that will be run after the illumination dot and scrim are shown.
- // The runnable is reset to null after it's executed once.
- @Nullable private Runnable mRunAfterShowingScrimAndDot;
-
- @NonNull private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
- @Override
- public void surfaceCreated(@NonNull SurfaceHolder holder) {
- Log.d(TAG, "Surface created");
- // SurfaceView sets this to true by default. We must set it to false to allow
- // onDraw to be called
- setWillNotDraw(false);
- }
-
- @Override
- public void surfaceChanged(@NonNull SurfaceHolder holder, int format,
- int width, int height) {
-
- }
-
- @Override
- public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- Log.d(TAG, "Surface destroyed");
- // Must not draw when the surface is destroyed
- setWillNotDraw(true);
- }
- };
public UdfpsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -126,35 +81,27 @@
a.recycle();
}
- getHolder().addCallback(mSurfaceCallback);
- getHolder().setFormat(PixelFormat.TRANSLUCENT);
+ // Inflate UdfpsSurfaceView
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ mHbmSurfaceView = (UdfpsSurfaceView) inflater.inflate(R.layout.udfps_surface_view,
+ null, false);
+ addView(mHbmSurfaceView);
+ mHbmSurfaceView.setVisibility(View.INVISIBLE);
- mScrimRect = new Rect();
- mScrimPaint = new Paint(0 /* flags */);
- mScrimPaint.setColor(Color.BLACK);
+ // Inflate UdfpsAnimationView
+ mAnimationView = (UdfpsAnimationView) inflater.inflate(R.layout.udfps_animation_view,
+ null, false);
+ mAnimationView.setParent(this);
+ addView(mAnimationView);
mSensorRect = new RectF();
- mSensorPaint = new Paint(0 /* flags */);
- mSensorPaint.setAntiAlias(true);
- mSensorPaint.setColor(Color.WHITE);
- mSensorPaint.setShadowLayer(SENSOR_SHADOW_RADIUS, 0, 0, Color.BLACK);
- mSensorPaint.setStyle(Paint.Style.FILL);
mDebugTextPaint = new Paint();
mDebugTextPaint.setAntiAlias(true);
mDebugTextPaint.setColor(Color.BLUE);
mDebugTextPaint.setTextSize(DEBUG_TEXT_SIZE_PX);
- mTouchableRegion = new Rect();
- // When the device is rotated, it's important that mTouchableRegion is updated before
- // this listener is called. This listener is usually called shortly after onLayout.
- mInsetsListener = internalInsetsInfo -> {
- internalInsetsInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- internalInsetsInfo.touchableRegion.set(mTouchableRegion);
- };
-
- mShowScrimAndDot = false;
+ mIlluminationRequested = false;
}
void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
@@ -162,15 +109,17 @@
}
void setUdfpsAnimation(@Nullable UdfpsAnimation animation) {
- mUdfpsAnimation = animation;
+ mAnimationView.setAnimation(animation);
}
+ @Override
+ public void setHbmCallback(@Nullable HbmCallback callback) {
+ mHbmSurfaceView.setHbmCallback(callback);
+ }
@Override
public void dozeTimeTick() {
- if (mUdfpsAnimation instanceof DozeReceiver) {
- ((DozeReceiver) mUdfpsAnimation).dozeTimeTick();
- }
+ mAnimationView.dozeTimeTick();
}
@Override
@@ -184,55 +133,20 @@
}
@Override
- public void onAlphaChanged(float alpha) {
- mNotificationPanelAlpha = (int) (alpha * 255);
- postInvalidate();
- }
-
- // The "h" and "w" are the display's height and width relative to its current rotation.
- protected void updateSensorRect(int h, int w) {
- // mSensorProps coordinates assume portrait mode.
- mSensorRect.set(mSensorProps.sensorLocationX - mSensorProps.sensorRadius,
- mSensorProps.sensorLocationY - mSensorProps.sensorRadius,
- mSensorProps.sensorLocationX + mSensorProps.sensorRadius,
- mSensorProps.sensorLocationY + mSensorProps.sensorRadius);
-
- // Transform mSensorRect if the device is in landscape mode.
- switch (mContext.getDisplay().getRotation()) {
- case Surface.ROTATION_90:
- //noinspection SuspiciousNameCombination
- mSensorRect.set(mSensorRect.top, h - mSensorRect.right, mSensorRect.bottom,
- h - mSensorRect.left);
- break;
- case Surface.ROTATION_270:
- //noinspection SuspiciousNameCombination
- mSensorRect.set(w - mSensorRect.bottom, mSensorRect.left, w - mSensorRect.top,
- mSensorRect.right);
- break;
- default:
- // Do nothing to stay in portrait mode.
- }
-
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.onSensorRectUpdated(new RectF(mSensorRect));
- }
+ public void onExpansionChanged(float expansion, boolean expanded) {
+ mAnimationView.onExpansionChanged(expansion, expanded);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- // Always re-compute the layout regardless of whether "changed" is true. It is usually false
- // when the device goes from landscape to seascape and vice versa, but mSensorRect and
- // its dependencies need to be recalculated to stay at the same physical location on the
- // screen.
- final int w = getLayoutParams().width;
- final int h = getLayoutParams().height;
- mScrimRect.set(0 /* left */, 0 /* top */, w, h);
- updateSensorRect(h, w);
- // Update mTouchableRegion with the rounded up values from mSensorRect. After "onLayout"
- // is finished, mTouchableRegion will be used by mInsetsListener to compute the touch
- // insets.
- mSensorRect.roundOut(mTouchableRegion);
+ mSensorRect.set(0 + mAnimationView.getPaddingX(),
+ 0 + mAnimationView.getPaddingY(),
+ 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingX(),
+ 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingY());
+
+ mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect));
+ mAnimationView.onSensorRectUpdated(new RectF(mSensorRect));
}
@Override
@@ -241,71 +155,34 @@
Log.v(TAG, "onAttachedToWindow");
// Retrieve the colors each time, since it depends on day/night mode
- updateColor();
-
- getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
- }
-
- private void updateColor() {
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.updateColor();
- }
+ mAnimationView.updateColor();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.v(TAG, "onDetachedFromWindow");
- getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
- if (mShowScrimAndDot && mIsHbmSupported) {
- // Only draw the scrim if HBM is supported.
- canvas.drawRect(mScrimRect, mScrimPaint);
- }
-
- if (!TextUtils.isEmpty(mDebugMessage)) {
- canvas.drawText(mDebugMessage, 0, 160, mDebugTextPaint);
- }
-
- if (mShowScrimAndDot) {
- // draw dot (white circle)
- canvas.drawOval(mSensorRect, mSensorPaint);
- } else {
- if (mUdfpsAnimation != null) {
- final int alpha = shouldPauseAuth() ? 255 - mNotificationPanelAlpha : 255;
- mUdfpsAnimation.setAlpha(alpha);
- mUdfpsAnimation.draw(canvas);
+ if (!mIlluminationRequested) {
+ if (!TextUtils.isEmpty(mDebugMessage)) {
+ canvas.drawText(mDebugMessage, 0, 160, mDebugTextPaint);
}
}
-
- if (mShowScrimAndDot && mRunAfterShowingScrimAndDot != null) {
- post(mRunAfterShowingScrimAndDot);
- mRunAfterShowingScrimAndDot = null;
- }
}
RectF getSensorRect() {
return new RectF(mSensorRect);
}
- void setHbmSupported(boolean value) {
- mIsHbmSupported = value;
- }
-
void setDebugMessage(String message) {
mDebugMessage = message;
postInvalidate();
}
- void setRunAfterShowingScrimAndDot(Runnable runnable) {
- mRunAfterShowingScrimAndDot = runnable;
- }
-
boolean isValidTouch(float x, float y, float pressure) {
// The X and Y coordinates of the sensor's center.
final float cx = mSensorRect.centerX();
@@ -326,39 +203,40 @@
* authentication which would cause the UDFPS icons to abruptly disappear, do it here by not
* sending onFingerDown and smoothly animating away.
*/
- private boolean shouldPauseAuth() {
+ boolean shouldPauseAuth() {
return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
|| mStatusBarState == SHADE_LOCKED
|| mStatusBarState == FULLSCREEN_USER_SWITCHER;
}
- void setScrimAlpha(int alpha) {
- mScrimPaint.setAlpha(alpha);
+ boolean isIlluminationRequested() {
+ return mIlluminationRequested;
}
- boolean isShowScrimAndDot() {
- return mShowScrimAndDot;
+ /**
+ * @param onIlluminatedRunnable Runs when the first illumination frame reaches the panel.
+ */
+ @Override
+ public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
+ mIlluminationRequested = true;
+ mAnimationView.setVisibility(View.INVISIBLE);
+ mHbmSurfaceView.setVisibility(View.VISIBLE);
+ mHbmSurfaceView.startIllumination(onIlluminatedRunnable);
}
- void showScrimAndDot() {
- mShowScrimAndDot = true;
- invalidate();
- }
-
- void hideScrimAndDot() {
- mShowScrimAndDot = false;
- invalidate();
+ @Override
+ public void stopIllumination() {
+ mIlluminationRequested = false;
+ mAnimationView.setVisibility(View.VISIBLE);
+ mHbmSurfaceView.setVisibility(View.INVISIBLE);
+ mHbmSurfaceView.stopIllumination();
}
void onEnrollmentProgress(int remaining) {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining);
- }
+ mAnimationView.onEnrollmentProgress(remaining);
}
void onEnrollmentHelp() {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
- }
+ mAnimationView.onEnrollmentHelp();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4d69700..b0067cd 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -29,6 +29,7 @@
import com.android.systemui.assist.AssistModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.controls.dagger.ControlsModule;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dump.DumpManager;
@@ -75,6 +76,7 @@
import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
+import java.util.concurrent.Executor;
import dagger.Binds;
import dagger.BindsOptionalOf;
@@ -153,6 +155,7 @@
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
+ // TODO: This should provided by the WM component
/** Provides Optional of BubbleManager */
@SysUISingleton
@Provides
@@ -166,11 +169,12 @@
ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager,
NotifPipeline notifPipeline, SysUiState sysUiState, FeatureFlags featureFlags,
- DumpManager dumpManager) {
+ DumpManager dumpManager, @Main Executor sysuiMainExecutor) {
return Optional.ofNullable(BubblesManager.create(context, bubblesOptional,
notificationShadeWindowController, statusBarStateController, shadeController,
configurationController, statusBarService, notificationManager,
interruptionStateProvider, zenModeController, notifUserManager,
- groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager));
+ groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager,
+ sysuiMainExecutor));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 34d1f6e..dab4d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -28,6 +28,7 @@
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
@@ -544,6 +545,7 @@
}
mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+ mNavigationBarView.setBehavior(mBehavior);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -664,7 +666,7 @@
}
private void initSecondaryHomeHandleForRotation() {
- if (!canShowSecondaryHandle()) {
+ if (mNavBarMode != NAV_BAR_MODE_GESTURAL) {
return;
}
@@ -918,6 +920,9 @@
}
if (mBehavior != behavior) {
mBehavior = behavior;
+ if (mNavigationBarView != null) {
+ mNavigationBarView.setBehavior(behavior);
+ }
updateSystemUiStateFlags(-1);
}
}
@@ -994,6 +999,8 @@
return MODE_LIGHTS_OUT_TRANSPARENT;
} else if ((appearance & APPEARANCE_OPAQUE_NAVIGATION_BARS) != 0) {
return MODE_OPAQUE;
+ } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS) != 0) {
+ return MODE_SEMI_TRANSPARENT;
} else {
return MODE_TRANSPARENT;
}
@@ -1517,7 +1524,7 @@
}
private boolean canShowSecondaryHandle() {
- return mNavBarMode == NAV_BAR_MODE_GESTURAL;
+ return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null;
}
private final Consumer<Integer> mRotationWatcher = rotation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index e7f2b222..c07404c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -57,6 +57,7 @@
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowInsets;
+import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -626,6 +627,10 @@
mRotationButtonController.onNavigationBarWindowVisibilityChange(visible);
}
+ public void setBehavior(@Behavior int behavior) {
+ mRotationButtonController.onBehaviorChanged(behavior);
+ }
+
@Override
public void setLayoutDirection(int layoutDirection) {
reloadNavIcons();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index df9e7a4..33d1807 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -35,6 +35,8 @@
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
+import android.view.WindowInsetsController;
+import android.view.WindowInsetsController.Behavior;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
@@ -78,6 +80,7 @@
private Consumer<Integer> mRotWatcherListener;
private boolean mListenersRegistered = false;
private boolean mIsNavigationBarShowing;
+ private @Behavior int mBehavior = WindowInsetsController.BEHAVIOR_DEFAULT;
private boolean mSkipOverrideUserLockPrefsOnce;
private int mLightIconColor;
private int mDarkIconColor;
@@ -297,8 +300,8 @@
}
mRotationButton.updateIcon(mLightIconColor, mDarkIconColor);
- if (mIsNavigationBarShowing) {
- // The navbar is visible so show the icon right away
+ if (canShowRotationButton()) {
+ // The navbar is visible / it's in visual immersive mode, so show the icon right away
showAndLogRotationSuggestion();
} else {
// If the navbar isn't shown, flag the rotate icon to be shown should the navbar become
@@ -318,14 +321,28 @@
void onNavigationBarWindowVisibilityChange(boolean showing) {
if (mIsNavigationBarShowing != showing) {
mIsNavigationBarShowing = showing;
-
- // If the navbar is visible, show the rotate button if there's a pending suggestion
- if (showing && mPendingRotationSuggestion) {
- showAndLogRotationSuggestion();
- }
+ showPendingRotationButtonIfNeeded();
}
}
+ void onBehaviorChanged(@Behavior int behavior) {
+ if (mBehavior != behavior) {
+ mBehavior = behavior;
+ showPendingRotationButtonIfNeeded();
+ }
+ }
+
+ private void showPendingRotationButtonIfNeeded() {
+ if (canShowRotationButton() && mPendingRotationSuggestion) {
+ showAndLogRotationSuggestion();
+ }
+ }
+
+ /** Return true when either the nav bar is visible or it's in visual immersive mode. */
+ private boolean canShowRotationButton() {
+ return mIsNavigationBarShowing || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT;
+ }
+
public Context getContext() {
return mContext;
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 66c535f..3f79be8 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -24,7 +24,6 @@
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowInsets
import android.widget.ImageView
@@ -66,9 +65,8 @@
super.onCreate(savedInstanceState)
window?.apply {
attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
- setLayout(MATCH_PARENT, WRAP_CONTENT)
+ setLayout(context.resources.getDimensionPixelSize(R.dimen.qs_panel_width), WRAP_CONTENT)
setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL)
- setBackgroundDrawable(null)
}
setContentView(R.layout.privacy_dialog)
@@ -125,12 +123,12 @@
val finalText = element.attribution?.let {
TextUtils.concat(
firstLine,
- "\n",
+ " ",
context.getString(R.string.ongoing_privacy_dialog_attribution_text, it)
)
} ?: firstLine
newView.requireViewById<TextView>(R.id.text).text = finalText
- newView.requireViewById<View>(R.id.link).apply {
+ newView.apply {
tag = element.type.permGroupName
setOnClickListener(clickListener)
}
@@ -154,9 +152,7 @@
}
private val clickListener = View.OnClickListener { v ->
- if (v.id == R.id.link) {
- v.tag?.let { activityStarter(it as String) }
- }
+ v.tag?.let { activityStarter(it as String) }
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index 99e9bfc..8b6c04f 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -227,21 +227,20 @@
/**
* Filters the list of elements to show.
*
- * * Return at most one element per [PrivacyType], sorted by the natural order of the
- * [PrivacyType].
- * * If there are no active usages for a type, return the most recent
- * * If there are multiple active usages for a type, return the most active recent.
+ * For each privacy type, it'll return all active elements. If there are no active elements,
+ * it'll return the most recent access
*/
private fun filterAndSelect(
list: List<PrivacyDialog.PrivacyElement>
): List<PrivacyDialog.PrivacyElement> {
- return list.groupBy { it.type }.toSortedMap().mapNotNull { entry ->
- if (entry.value.isEmpty()) {
- null
+ return list.groupBy { it.type }.toSortedMap().flatMap { (_, elements) ->
+ val actives = elements.filter { it.active }
+ if (actives.isNotEmpty()) {
+ actives.sortedByDescending { it.lastActiveTimestamp }
} else {
- val actives = entry.value.filter { it.active }
- val out = if (actives.isNotEmpty()) actives else entry.value
- out.maxByOrNull { it.lastActiveTimestamp }
+ elements.maxByOrNull { it.lastActiveTimestamp }?.let {
+ listOf(it)
+ } ?: emptyList()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index b6e07b1..3841dac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -36,6 +36,7 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -49,9 +50,10 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- IndividualSensorPrivacyController sensorPrivacyController) {
+ IndividualSensorPrivacyController sensorPrivacyController,
+ KeyguardStateController keyguardStateController) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger, sensorPrivacyController);
+ activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 1b2ad4c..191b85b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -54,6 +54,9 @@
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.WifiIcons;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
import javax.inject.Inject;
/** Quick settings tile: Internet **/
@@ -65,7 +68,7 @@
protected final NetworkController mController;
private final DataUsageController mDataController;
private final QSTile.SignalState mStateBeforeClick = newTileState();
- // The last updated tile state, 0: mobile, 1: wifi
+ // The last updated tile state, 0: mobile, 1: wifi, 2: ethernet.
private int mLastTileState = -1;
protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
@@ -140,6 +143,21 @@
return string;
}
+ private static final class EthernetCallbackInfo {
+ boolean mConnected;
+ int mEthernetSignalIconId;
+ String mEthernetContentDescription;
+
+ @Override
+ public String toString() {
+ return new StringBuilder("EthernetCallbackInfo[")
+ .append("mConnected=").append(mConnected)
+ .append(",mEthernetSignalIconId=").append(mEthernetSignalIconId)
+ .append(",mEthernetContentDescription=").append(mEthernetContentDescription)
+ .append(']').toString();
+ }
+ }
+
private static final class WifiCallbackInfo {
boolean mAirplaneModeEnabled;
boolean mEnabled;
@@ -212,6 +230,8 @@
protected final class InternetSignalCallback implements SignalCallback {
final WifiCallbackInfo mWifiInfo = new WifiCallbackInfo();
final CellularCallbackInfo mCellularInfo = new CellularCallbackInfo();
+ final EthernetCallbackInfo mEthernetInfo = new EthernetCallbackInfo();
+
@Override
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
@@ -230,14 +250,12 @@
}
// When airplane mode is enabled, we need to refresh the Internet Tile even if the WiFi
// is not the default network.
- if (qsIcon == null && !mWifiInfo.mAirplaneModeEnabled) {
+ if (qsIcon == null) {
return;
}
- if (qsIcon != null) {
- mWifiInfo.mConnected = qsIcon.visible;
- mWifiInfo.mWifiSignalIconId = qsIcon.icon;
- mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
- }
+ mWifiInfo.mConnected = qsIcon.visible;
+ mWifiInfo.mWifiSignalIconId = qsIcon.icon;
+ mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
mWifiInfo.mEnabled = enabled;
mWifiInfo.mSsid = description;
mWifiInfo.mActivityIn = activityIn;
@@ -287,6 +305,20 @@
}
@Override
+ public void setEthernetIndicators(IconState icon) {
+ if (DEBUG) {
+ Log.d(TAG, "setEthernetIndicators: "
+ + "icon = " + (icon == null ? "" : icon.toString()));
+ }
+ mEthernetInfo.mConnected = icon.visible;
+ mEthernetInfo.mEthernetSignalIconId = icon.icon;
+ mEthernetInfo.mEthernetContentDescription = icon.contentDescription;
+ if (icon.visible) {
+ refreshState(mEthernetInfo);
+ }
+ }
+
+ @Override
public void setNoSims(boolean show, boolean simDetected) {
if (DEBUG) {
Log.d(TAG, "setNoSims: "
@@ -299,7 +331,6 @@
mCellularInfo.mMobileSignalIconId = 0;
mCellularInfo.mQsTypeIcon = 0;
}
- refreshState(mCellularInfo);
}
@Override
@@ -310,7 +341,9 @@
}
mCellularInfo.mAirplaneModeEnabled = icon.visible;
mWifiInfo.mAirplaneModeEnabled = icon.visible;
- refreshState(mCellularInfo);
+ if (!mSignalCallback.mEthernetInfo.mConnected) {
+ refreshState(mCellularInfo);
+ }
}
@Override
@@ -330,6 +363,15 @@
mWifiInfo.mNoNetworksAvailable = noNetworksAvailable;
refreshState(mWifiInfo);
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder("InternetSignalCallback[")
+ .append("mWifiInfo=").append(mWifiInfo)
+ .append(",mCellularInfo=").append(mCellularInfo)
+ .append(",mEthernetInfo=").append(mEthernetInfo)
+ .append(']').toString();
+ }
}
@Override
@@ -340,6 +382,9 @@
} else if (arg instanceof WifiCallbackInfo) {
mLastTileState = 1;
handleUpdateWifiState(state, arg);
+ } else if (arg instanceof EthernetCallbackInfo) {
+ mLastTileState = 2;
+ handleUpdateEthernetState(state, arg);
} else {
// handleUpdateState will be triggered when user expands the QuickSetting panel with
// arg = null, in this case the last updated CellularCallbackInfo or WifiCallbackInfo
@@ -348,6 +393,8 @@
handleUpdateCellularState(state, mSignalCallback.mCellularInfo);
} else if (mLastTileState == 1) {
handleUpdateWifiState(state, mSignalCallback.mWifiInfo);
+ } else if (mLastTileState == 2) {
+ handleUpdateEthernetState(state, mSignalCallback.mEthernetInfo);
}
}
}
@@ -440,7 +487,6 @@
Log.d(TAG, "handleUpdateCellularState: " + "CellularCallbackInfo = " + cb.toString());
}
final Resources r = mContext.getResources();
- // TODO(b/174753536): Use the new "Internet" string as state.label once available.
state.label = r.getString(R.string.quick_settings_internet_label);
state.state = Tile.STATE_ACTIVE;
boolean mobileDataEnabled = mDataController.isMobileDataSupported()
@@ -478,6 +524,18 @@
}
}
+ private void handleUpdateEthernetState(SignalState state, Object arg) {
+ EthernetCallbackInfo cb = (EthernetCallbackInfo) arg;
+ if (DEBUG) {
+ Log.d(TAG, "handleUpdateEthernetState: " + "EthernetCallbackInfo = " + cb.toString());
+ }
+ final Resources r = mContext.getResources();
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ state.state = Tile.STATE_ACTIVE;
+ state.icon = ResourceIcon.get(cb.mEthernetSignalIconId);
+ state.secondaryLabel = cb.mEthernetContentDescription;
+ }
+
private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
if (TextUtils.isEmpty(dataType)) {
return Html.fromHtml((current == null ? "" : current.toString()), 0);
@@ -519,4 +577,15 @@
return d;
}
}
+
+ /**
+ * Dumps the state of this tile along with its name.
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(this.getClass().getSimpleName() + ":");
+ pw.print(" "); pw.println(getState().toString());
+ pw.print(" "); pw.println("mLastTileState=" + mLastTileState);
+ pw.print(" "); pw.println("mSignalCallback=" + mSignalCallback.toString());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index 9cc6f09..2f0071a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -36,6 +36,7 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -49,9 +50,10 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- IndividualSensorPrivacyController sensorPrivacyController) {
+ IndividualSensorPrivacyController sensorPrivacyController,
+ KeyguardStateController keyguardStateController) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger, sensorPrivacyController);
+ activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 12205d6..00703e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -35,6 +35,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
/**
* Superclass to toggle individual sensor privacy via quick settings tiles
@@ -42,6 +43,7 @@
public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanState> implements
IndividualSensorPrivacyController.Callback {
+ private final KeyguardStateController mKeyguard;
private IndividualSensorPrivacyController mSensorPrivacyController;
/**
@@ -61,10 +63,12 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- IndividualSensorPrivacyController sensorPrivacyController) {
+ IndividualSensorPrivacyController sensorPrivacyController,
+ KeyguardStateController keyguardStateController) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
activityStarter, qsLogger);
mSensorPrivacyController = sensorPrivacyController;
+ mKeyguard = keyguardStateController;
mSensorPrivacyController.observe(getLifecycle(), this);
}
@@ -75,6 +79,13 @@
@Override
protected void handleClick() {
+ if (mKeyguard.isMethodSecure() && mKeyguard.isShowing()) {
+ mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+ mSensorPrivacyController.setSensorBlocked(getSensorId(),
+ !mSensorPrivacyController.isSensorBlocked(getSensorId()));
+ });
+ return;
+ }
mSensorPrivacyController.setSensorBlocked(getSensorId(),
!mSensorPrivacyController.isSensorBlocked(getSensorId()));
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
new file mode 100644
index 0000000..8e182b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -0,0 +1,151 @@
+/*
+ * 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.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * CropView has top and bottom draggable crop handles, with a scrim to darken the areas being
+ * cropped out.
+ */
+public class CropView extends View {
+ private enum CropBoundary {
+ NONE, TOP, BOTTOM
+ }
+
+ private final float mCropTouchMargin;
+ private final Paint mShadePaint;
+ private final Paint mHandlePaint;
+
+ // Top and bottom crops are stored as floats [0, 1], representing the top and bottom of the
+ // view, respectively.
+ private float mTopCrop = 0f;
+ private float mBottomCrop = 1f;
+
+ private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE;
+ private float mLastY;
+
+ public CropView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ TypedArray t = context.getTheme().obtainStyledAttributes(
+ attrs, R.styleable.CropView, 0, 0);
+ mShadePaint = new Paint();
+ mShadePaint.setColor(t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT));
+ mHandlePaint = new Paint();
+ mHandlePaint.setColor(t.getColor(R.styleable.CropView_handleColor, Color.BLACK));
+ mHandlePaint.setStrokeWidth(
+ t.getDimensionPixelSize(R.styleable.CropView_handleThickness, 20));
+ t.recycle();
+ // 48 dp touchable region around each handle.
+ mCropTouchMargin = 24 * getResources().getDisplayMetrics().density;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ drawShade(canvas, 0, mTopCrop);
+ drawShade(canvas, mBottomCrop, 1f);
+ drawHandle(canvas, mTopCrop);
+ drawHandle(canvas, mBottomCrop);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ int topPx = fractionToPixels(mTopCrop);
+ int bottomPx = fractionToPixels(mBottomCrop);
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ mLastY = event.getY();
+ }
+ return true;
+ }
+ if (event.getAction() == MotionEvent.ACTION_MOVE
+ && mCurrentDraggingBoundary != CropBoundary.NONE) {
+ float delta = event.getY() - mLastY;
+ if (mCurrentDraggingBoundary == CropBoundary.TOP) {
+ mTopCrop = pixelsToFraction((int) MathUtils.constrain(topPx + delta, 0,
+ bottomPx - 2 * mCropTouchMargin));
+ } else { // Bottom
+ mBottomCrop = pixelsToFraction((int) MathUtils.constrain(bottomPx + delta,
+ topPx + 2 * mCropTouchMargin, getMeasuredHeight()));
+ }
+ mLastY = event.getY();
+ invalidate();
+ return true;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ /**
+ * @return value [0,1] representing the position of the top crop boundary.
+ */
+ public float getTopBoundary() {
+ return mTopCrop;
+ }
+
+ /**
+ * @return value [0,1] representing the position of the bottom crop boundary.
+ */
+ public float getBottomBoundary() {
+ return mBottomCrop;
+ }
+
+ private void drawShade(Canvas canvas, float fracStart, float fracEnd) {
+ canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(),
+ fractionToPixels(fracEnd), mShadePaint);
+ }
+
+ private void drawHandle(Canvas canvas, float frac) {
+ int y = fractionToPixels(frac);
+ canvas.drawLine(0, y, getMeasuredWidth(), y, mHandlePaint);
+ }
+
+ private int fractionToPixels(float frac) {
+ return (int) (frac * getMeasuredHeight());
+ }
+
+ private float pixelsToFraction(int px) {
+ return px / (float) getMeasuredHeight();
+ }
+
+ private CropBoundary nearestBoundary(MotionEvent event, int topPx, int bottomPx) {
+ if (Math.abs(event.getY() - topPx) < mCropTouchMargin) {
+ return CropBoundary.TOP;
+ }
+ if (Math.abs(event.getY() - bottomPx) < mCropTouchMargin) {
+ return CropBoundary.BOTTOM;
+ }
+ return CropBoundary.NONE;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 8ff66f5..20f8451 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -108,6 +108,15 @@
}
Bitmap toBitmap() {
+ return toBitmap(new Rect(0, 0, getWidth(), getHeight()));
+ }
+
+ /**
+ * @param bounds Selected portion of the tile set's bounds (equivalent to tile bounds coord
+ * space). For example, to get the whole doc, use Rect(0, 0, getWidth(),
+ * getHeight()).
+ */
+ Bitmap toBitmap(Rect bounds) {
if (mTiles.isEmpty()) {
return null;
}
@@ -115,6 +124,9 @@
output.setPosition(0, 0, getWidth(), getHeight());
RecordingCanvas canvas = output.beginRecording();
canvas.translate(-getLeft(), -getTop());
+ // Additional translation to account for the requested bounds
+ canvas.translate(-bounds.left, -bounds.top);
+ canvas.clipRect(bounds);
for (ImageTile tile : mTiles) {
canvas.save();
canvas.translate(tile.getLeft(), tile.getTop());
@@ -122,7 +134,7 @@
canvas.restore();
}
output.endRecording();
- return HardwareRenderer.createHardwareBitmap(output, getWidth(), getHeight());
+ return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
}
int getLeft() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index d6413ed..953b40b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -388,12 +388,6 @@
}
});
- // ignore system bar insets for the purpose of window layout
- mScreenshotView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
- new WindowInsets.Builder(insets)
- .setInsets(WindowInsets.Type.all(), Insets.NONE)
- .build()));
-
// TODO(159460485): Remove this when focus is handled properly in the system
mScreenshotView.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
@@ -533,9 +527,6 @@
attachWindow();
- if (DEBUG_WINDOW) {
- Log.d(TAG, "setContentView: " + mScreenshotView);
- }
mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -549,7 +540,13 @@
}
});
mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setContentView: " + mScreenshotView);
+ }
setContentView(mScreenshotView);
+ // ignore system bar insets for the purpose of window layout
+ mWindow.getDecorView().setOnApplyWindowInsetsListener(
+ (v, insets) -> WindowInsets.CONSUMED);
cancelTimeout(); // restarted after animation
}
@@ -602,7 +599,7 @@
private void runScrollCapture(ScrollCaptureClient.Connection connection) {
cancelTimeout();
ScrollCaptureController controller = new ScrollCaptureController(mContext, connection,
- mMainExecutor, mBgExecutor, mImageExporter);
+ mMainExecutor, mBgExecutor, mImageExporter, mUiEventLogger);
controller.attach(mWindow);
controller.start(new TakeScreenshotService.RequestCallback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index f1fb688..5cf0188 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -63,7 +63,15 @@
@UiEvent(doc = "screenshot swiped to dismiss")
SCREENSHOT_SWIPE_DISMISSED(656),
@UiEvent(doc = "screenshot reentered for new screenshot")
- SCREENSHOT_REENTERED(640);
+ SCREENSHOT_REENTERED(640),
+ @UiEvent(doc = "Long screenshot button was shown to the user")
+ SCREENSHOT_LONG_SCREENSHOT_IMPRESSION(687),
+ @UiEvent(doc = "User has requested a long screenshot")
+ SCREENSHOT_LONG_SCREENSHOT_REQUESTED(688),
+ @UiEvent(doc = "User has shared a long screenshot")
+ SCREENSHOT_LONG_SCREENSHOT_SHARE(689),
+ @UiEvent(doc = "User has sent a long screenshot to the editor")
+ SCREENSHOT_LONG_SCREENSHOT_EDIT(690);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index bf86b68..3bc5ebf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -192,14 +192,14 @@
if (DEBUG_SCROLL) {
Log.d(TAG, "Showing Scroll option");
}
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION);
mScrollChip.setVisibility(VISIBLE);
mScrollChip.setOnClickListener((v) -> {
if (DEBUG_INPUT) {
Log.d(TAG, "scroll chip tapped");
}
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED);
onClick.run();
- // TODO Logging, store event consumer to a field
- //onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 9be3566..18c379a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -18,10 +18,13 @@
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.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver.InternalInsetsInfo;
@@ -29,6 +32,7 @@
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.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
@@ -48,6 +52,13 @@
public class ScrollCaptureController implements OnComputeInternalInsetsListener {
private static final String TAG = "ScrollCaptureController";
+ // TODO: Support saving without additional action.
+ private enum PendingAction {
+ SHARE,
+ EDIT,
+ SAVE
+ }
+
public static final int MAX_PAGES = 5;
public static final int MAX_HEIGHT = 12000;
@@ -58,26 +69,27 @@
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 mClose;
+ private View mSave;
+ private View mCancel;
private View mEdit;
private View mShare;
-
- private ListenableFuture<ImageExporter.Result> mExportFuture;
- private Runnable mPendingAction;
+ private CropView mCropView;
public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
- Executor bgExecutor, ImageExporter exporter) {
+ Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
mContext = context;
mConnection = connection;
mUiExecutor = uiExecutor;
mBgExecutor = bgExecutor;
mImageExporter = exporter;
+ mUiEventLogger = uiEventLogger;
mImageTileSet = new ImageTileSet();
}
@@ -103,11 +115,14 @@
.addOnComputeInternalInsetsListener(this);
mPreview = findViewById(R.id.preview);
- mClose = findViewById(R.id.close);
+ 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);
- mClose.setOnClickListener(this::onClicked);
+ mSave.setOnClickListener(this::onClicked);
+ mCancel.setOnClickListener(this::onClicked);
mEdit.setOnClickListener(this::onClicked);
mShare.setOnClickListener(this::onClicked);
@@ -122,7 +137,8 @@
}
void disableButtons() {
- mClose.setEnabled(false);
+ mSave.setEnabled(false);
+ mCancel.setEnabled(false);
mEdit.setEnabled(false);
mShare.setEnabled(false);
}
@@ -131,26 +147,18 @@
Log.d(TAG, "button clicked!");
int id = v.getId();
- if (id == R.id.close) {
- v.setPressed(true);
- disableButtons();
- finish();
- } else if (id == R.id.edit) {
- v.setPressed(true);
- disableButtons();
- edit();
- } else if (id == R.id.share) {
- v.setPressed(true);
- disableButtons();
- share();
- }
- }
-
- private void finish() {
- if (mExportFuture == null) {
+ v.setPressed(true);
+ disableButtons();
+ if (id == R.id.save) {
+ startExport(PendingAction.SAVE);
+ } else if (id == R.id.cancel) {
doFinish();
- } else {
- mExportFuture.addListener(this::doFinish, mUiExecutor);
+ } 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);
}
}
@@ -162,30 +170,55 @@
.removeOnComputeInternalInsetsListener(this);
}
- private void edit() {
- sendIntentWhenReady(Intent.ACTION_EDIT);
- }
-
- private void share() {
- sendIntentWhenReady(Intent.ACTION_SEND);
- }
-
- void sendIntentWhenReady(String action) {
- if (mExportFuture != null) {
- mExportFuture.addListener(() -> {
- try {
- ImageExporter.Result result = mExportFuture.get();
- sendIntent(action, result.uri);
- mCallback.onFinish();
- } catch (InterruptedException | ExecutionException e) {
- Log.e(TAG, "failed to export", e);
- mCallback.onFinish();
+ private void startExport(PendingAction action) {
+ Rect croppedPortion = new Rect(
+ 0,
+ (int) (mImageTileSet.getHeight() * mCropView.getTopBoundary()),
+ mImageTileSet.getWidth(),
+ (int) (mImageTileSet.getHeight() * mCropView.getBottomBoundary()));
+ 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();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "failed to export", e);
+ mCallback.onFinish();
+ }
+ }, mUiExecutor);
+ }
- }, mUiExecutor);
- } else {
- mPendingAction = this::edit;
+ 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) {
@@ -240,21 +273,6 @@
session.end(mCallback::onFinish);
} else {
mPreview.setImageDrawable(mImageTileSet.getDrawable());
- mExportFuture = mImageExporter.export(
- mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime);
- // The user chose an action already, link it to the result
- if (mPendingAction != null) {
- mExportFuture.addListener(mPendingAction, mUiExecutor);
- }
}
}
-
- void sendIntent(String action, Uri uri) {
- Intent editIntent = new Intent(action);
- editIntent.setType("image/png");
- editIntent.setData(uri);
- editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivityAsUser(editIntent, UserHandle.CURRENT);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index c1161f1..d707bca0 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -17,14 +17,19 @@
package com.android.systemui.sensorprivacy
import android.app.AppOpsManager
+import android.app.KeyguardManager
+import android.app.KeyguardManager.KeyguardDismissCallback
import android.content.DialogInterface
import android.content.Intent.EXTRA_PACKAGE_NAME
import android.content.pm.PackageManager
import android.content.res.Resources
import android.hardware.SensorPrivacyManager
-import android.hardware.SensorPrivacyManager.*
+import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
+import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA
+import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE
import android.os.Bundle
import android.text.Html
+import android.util.Log
import com.android.internal.app.AlertActivity
import com.android.systemui.R
@@ -35,18 +40,29 @@
* <p>The dialog is started for the user the app is running for which might be a secondary users.
*/
class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListener {
+
+ companion object {
+ private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
+ }
+
private var sensor = -1
private lateinit var sensorUsePackageName: String
private lateinit var sensorPrivacyManager: SensorPrivacyManager
private lateinit var appOpsManager: AppOpsManager
+ private lateinit var keyguardManager: KeyguardManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ setShowWhenLocked(true)
+
+ setFinishOnTouchOutside(false)
+
setResult(RESULT_CANCELED)
sensorPrivacyManager = getSystemService(SensorPrivacyManager::class.java)!!
appOpsManager = getSystemService(AppOpsManager::class.java)!!
+ keyguardManager = getSystemService(KeyguardManager::class.java)!!
sensorUsePackageName = intent.getStringExtra(EXTRA_PACKAGE_NAME) ?: return
sensor = intent.getIntExtra(EXTRA_SENSOR, -1).also {
@@ -107,8 +123,23 @@
override fun onClick(dialog: DialogInterface?, which: Int) {
when (which) {
BUTTON_POSITIVE -> {
- sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
- setResult(RESULT_OK)
+ if (keyguardManager.isDeviceLocked) {
+ keyguardManager
+ .requestDismissKeyguard(this, object : KeyguardDismissCallback() {
+ override fun onDismissError() {
+ Log.e(LOG_TAG, "Cannot dismiss keyguard")
+ }
+
+ override fun onDismissSucceeded() {
+ sensorPrivacyManager
+ .setIndividualSensorPrivacyForProfileGroup(sensor, false)
+ setResult(RESULT_OK)
+ }
+ })
+ } else {
+ sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
+ setResult(RESULT_OK)
+ }
}
}
@@ -120,4 +151,8 @@
sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
}
+
+ override fun onBackPressed() {
+ // do not allow backing out
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
index 11ee94c..8dcc8b4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
@@ -17,6 +17,7 @@
package com.android.systemui.settings.brightness;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
@@ -28,11 +29,13 @@
public class BrightnessControllerSettings {
private static final String THICK_BRIGHTNESS_SLIDER = "sysui_thick_brightness";
+ private final FeatureFlags mFeatureFlags;
private final boolean mUseThickSlider;
private final boolean mUseMirrorOnThickSlider;
@Inject
- public BrightnessControllerSettings(SecureSettings settings) {
+ public BrightnessControllerSettings(SecureSettings settings, FeatureFlags featureFlags) {
+ mFeatureFlags = featureFlags;
mUseThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 0;
mUseMirrorOnThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 2;
}
@@ -41,11 +44,11 @@
// restart systemui after changing it.
/** */
boolean useThickSlider() {
- return mUseThickSlider;
+ return mUseThickSlider && mFeatureFlags.useNewBrightnessSlider();
}
/** */
boolean useMirrorOnThickSlider() {
- return mUseMirrorOnThickSlider;
+ return !useThickSlider() || (useThickSlider() && mUseMirrorOnThickSlider);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 30b3158..e7b60c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -61,4 +61,13 @@
public boolean isKeyguardLayoutEnabled() {
return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
}
+
+ /** b/178485354 */
+ public boolean useNewBrightnessSlider() {
+ return mFlagReader.isEnabled(R.bool.flag_brightness_slider);
+ }
+
+ public boolean isPeopleTileEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_conversations);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index 7f30009..6023b7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -26,25 +26,41 @@
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
import android.util.AttributeSet;
import android.view.View;
import androidx.core.graphics.ColorUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.drawable.ScrimDrawable;
+import java.util.concurrent.Executor;
+
+
/**
- * A view which can draw a scrim
+ * A view which can draw a scrim. This view maybe be used in multiple windows running on different
+ * threads, but is controlled by {@link com.android.systemui.statusbar.phone.ScrimController} so we
+ * need to be careful to synchronize when necessary.
*/
public class ScrimView extends View {
+ private final Object mColorLock = new Object();
+
+ @GuardedBy("mColorLock")
private final ColorExtractor.GradientColors mColors;
+ // Used only for returning the colors
+ private final ColorExtractor.GradientColors mTmpColors = new ColorExtractor.GradientColors();
private float mViewAlpha = 1.0f;
private Drawable mDrawable;
private PorterDuffColorFilter mColorFilter;
private int mTintColor;
private Runnable mChangeRunnable;
+ private Executor mChangeRunnableExecutor;
+ private Executor mExecutor;
+ private Looper mExecutorLooper;
public ScrimView(Context context) {
this(context, null);
@@ -64,7 +80,16 @@
mDrawable = new ScrimDrawable();
mDrawable.setCallback(this);
mColors = new ColorExtractor.GradientColors();
- updateColorWithTint(false);
+ mExecutorLooper = Looper.myLooper();
+ mExecutor = Runnable::run;
+ executeOnExecutor(() -> {
+ updateColorWithTint(false);
+ });
+ }
+
+ public void setExecutor(Executor executor, Looper looper) {
+ mExecutor = executor;
+ mExecutorLooper = looper;
}
@Override
@@ -75,11 +100,13 @@
}
public void setDrawable(Drawable drawable) {
- mDrawable = drawable;
- mDrawable.setCallback(this);
- mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom());
- mDrawable.setAlpha((int) (255 * mViewAlpha));
- invalidate();
+ executeOnExecutor(() -> {
+ mDrawable = drawable;
+ mDrawable.setCallback(this);
+ mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom());
+ mDrawable.setAlpha((int) (255 * mViewAlpha));
+ invalidate();
+ });
}
@Override
@@ -99,6 +126,13 @@
}
}
+ @Override
+ public void setClickable(boolean clickable) {
+ executeOnExecutor(() -> {
+ super.setClickable(clickable);
+ });
+ }
+
public void setColors(@NonNull ColorExtractor.GradientColors colors) {
setColors(colors, false);
}
@@ -107,11 +141,15 @@
if (colors == null) {
throw new IllegalArgumentException("Colors cannot be null");
}
- if (mColors.equals(colors)) {
- return;
- }
- mColors.set(colors);
- updateColorWithTint(animated);
+ executeOnExecutor(() -> {
+ synchronized(mColorLock) {
+ if (mColors.equals(colors)) {
+ return;
+ }
+ mColors.set(colors);
+ }
+ updateColorWithTint(animated);
+ });
}
@VisibleForTesting
@@ -120,7 +158,10 @@
}
public ColorExtractor.GradientColors getColors() {
- return mColors;
+ synchronized(mColorLock) {
+ mTmpColors.set(mColors);
+ }
+ return mTmpColors;
}
public void setTint(int color) {
@@ -128,11 +169,13 @@
}
public void setTint(int color, boolean animated) {
- if (mTintColor == color) {
- return;
- }
- mTintColor = color;
- updateColorWithTint(animated);
+ executeOnExecutor(() -> {
+ if (mTintColor == color) {
+ return;
+ }
+ mTintColor = color;
+ updateColorWithTint(animated);
+ });
}
private void updateColorWithTint(boolean animated) {
@@ -160,7 +203,7 @@
}
if (mChangeRunnable != null) {
- mChangeRunnable.run();
+ mChangeRunnableExecutor.execute(mChangeRunnable);
}
}
@@ -184,26 +227,37 @@
if (isNaN(alpha)) {
throw new IllegalArgumentException("alpha cannot be NaN: " + alpha);
}
- if (alpha != mViewAlpha) {
- mViewAlpha = alpha;
+ executeOnExecutor(() -> {
+ if (alpha != mViewAlpha) {
+ mViewAlpha = alpha;
- mDrawable.setAlpha((int) (255 * alpha));
- if (mChangeRunnable != null) {
- mChangeRunnable.run();
+ mDrawable.setAlpha((int) (255 * alpha));
+ if (mChangeRunnable != null) {
+ mChangeRunnableExecutor.execute(mChangeRunnable);
+ }
}
- }
+ });
}
public float getViewAlpha() {
return mViewAlpha;
}
- public void setChangeRunnable(Runnable changeRunnable) {
+ public void setChangeRunnable(Runnable changeRunnable, Executor changeRunnableExecutor) {
mChangeRunnable = changeRunnable;
+ mChangeRunnableExecutor = changeRunnableExecutor;
}
@Override
protected boolean canReceivePointerEvents() {
return false;
}
+
+ private void executeOnExecutor(Runnable r) {
+ if (mExecutor == null || Looper.myLooper() == mExecutorLooper) {
+ r.run();
+ } else {
+ mExecutor.execute(r);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
index 1ec043c..e4ae560 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
@@ -23,8 +23,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
-import com.android.systemui.statusbar.phone.LockIcon;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
@@ -41,7 +39,6 @@
private final Context mContext;
private final InjectionInflationController mInjectionInflationController;
- private final LockscreenLockIconController mLockIconController;
private final NotificationShelfComponent.Builder mNotificationShelfComponentBuilder;
private NotificationShadeWindowView mNotificationShadeWindowView;
@@ -51,11 +48,9 @@
@Inject
public SuperStatusBarViewFactory(Context context,
InjectionInflationController injectionInflationController,
- NotificationShelfComponent.Builder notificationShelfComponentBuilder,
- LockscreenLockIconController lockIconController) {
+ NotificationShelfComponent.Builder notificationShelfComponentBuilder) {
mContext = context;
mInjectionInflationController = injectionInflationController;
- mLockIconController = lockIconController;
mNotificationShelfComponentBuilder = notificationShelfComponentBuilder;
}
@@ -77,10 +72,6 @@
throw new IllegalStateException(
"R.layout.super_notification_shade could not be properly inflated");
}
- LockIcon lockIcon = mNotificationShadeWindowView.findViewById(R.id.lock_icon);
- if (lockIcon != null) {
- mLockIconController.attach(lockIcon);
- }
return mNotificationShadeWindowView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 4fde118..db49e44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -21,7 +21,6 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -50,7 +49,6 @@
private final ShadeListBuilder mListBuilder;
private final NotifCoordinators mNotifPluggableCoordinators;
private final NotifInflaterImpl mNotifInflater;
- private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
private final DumpManager mDumpManager;
private final ShadeViewManagerFactory mShadeViewManagerFactory;
private final FeatureFlags mFeatureFlags;
@@ -64,7 +62,6 @@
ShadeListBuilder listBuilder,
NotifCoordinators notifCoordinators,
NotifInflaterImpl notifInflater,
- PeopleSpaceWidgetManager peopleSpaceWidgetManager,
DumpManager dumpManager,
ShadeViewManagerFactory shadeViewManagerFactory,
FeatureFlags featureFlags) {
@@ -75,7 +72,6 @@
mNotifPluggableCoordinators = notifCoordinators;
mDumpManager = dumpManager;
mNotifInflater = notifInflater;
- mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
mShadeViewManagerFactory = shadeViewManagerFactory;
mFeatureFlags = featureFlags;
}
@@ -103,7 +99,6 @@
mListBuilder.attach(mNotifCollection);
mNotifCollection.attach(mGroupCoalescer);
mGroupCoalescer.attach(notificationService);
- mPeopleSpaceWidgetManager.attach(notificationService);
Log.d(TAG, "Notif pipeline initialized");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 93156d8..0957f78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -69,6 +69,8 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
+import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
@@ -89,6 +91,10 @@
*/
@Module(includes = { NotificationSectionHeadersModule.class })
public interface NotificationsModule {
+ @Binds
+ StackScrollAlgorithm.SectionProvider bindSectionProvider(
+ NotificationSectionsManager impl);
+
/** Provides an instance of {@link NotificationEntryManager} */
@SysUISingleton
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 54ce4ed..0ad6507 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -18,6 +18,7 @@
import android.service.notification.StatusBarNotification
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.NotificationListener
@@ -73,7 +74,8 @@
private val headsUpController: HeadsUpController,
private val headsUpViewBinder: HeadsUpViewBinder,
private val clickerBuilder: NotificationClicker.Builder,
- private val animatedImageNotificationManager: AnimatedImageNotificationManager
+ private val animatedImageNotificationManager: AnimatedImageNotificationManager,
+ private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager
) : NotificationsController {
override fun initialize(
@@ -126,6 +128,10 @@
entryManager.attach(notificationListener)
}
+
+ if (featureFlags.isPeopleTileEnabled) {
+ peopleSpaceWidgetManager.attach(notificationListener)
+ }
}
override fun dump(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index a12179c..756fe6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -22,6 +22,7 @@
import android.util.MathUtils;
import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -30,9 +31,12 @@
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider;
+import javax.inject.Inject;
+
/**
* A global state to track all input states for the algorithm.
*/
+@SysUISingleton
public class AmbientState {
private static final float MAX_PULSE_HEIGHT = 100000f;
@@ -83,6 +87,7 @@
/** Tracks the state from AlertingNotificationManager#hasNotifications() */
private boolean mHasAlertEntries;
+ @Inject
public AmbientState(
Context context,
@NonNull SectionProvider sectionProvider) {
@@ -98,7 +103,7 @@
mBaseZHeight = getBaseHeight(mZDistanceBetweenElements);
}
- void setIsShadeOpening(boolean isOpening) {
+ public void setIsShadeOpening(boolean isOpening) {
mIsShadeOpening = isOpening;
}
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 3f3be44..2c7c5cc 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
@@ -491,7 +491,8 @@
NotificationSectionsManager notificationSectionsManager,
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
- SysuiStatusBarStateController statusbarStateController
+ SysuiStatusBarStateController statusbarStateController,
+ AmbientState ambientState
) {
super(context, attrs, 0, 0);
Resources res = getResources();
@@ -500,7 +501,7 @@
mSectionsManager.initialize(this, LayoutInflater.from(context));
mSections = mSectionsManager.createSectionsForBuckets();
- mAmbientState = new AmbientState(context, mSectionsManager);
+ mAmbientState = ambientState;
mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
.getDefaultColor();
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
@@ -549,10 +550,6 @@
}
}
- void setIsShadeOpening(boolean isOpening) {
- mAmbientState.setIsShadeOpening(isOpening);
- }
-
void setSectionPadding(float margin) {
mAmbientState.setSectionPadding(margin);
requestChildrenUpdate();
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 879dad8..a516742 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
@@ -45,7 +45,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
-import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
@@ -271,10 +270,6 @@
}
};
- public void setIsShadeOpening(boolean isOpening) {
- mView.setIsShadeOpening(isOpening);
- }
-
public void setSectionPadding(float padding) {
mView.setSectionPadding(padding);
}
@@ -851,11 +846,14 @@
return mView.getChildAtRawPosition(x, y);
}
- public FrameLayout.LayoutParams getLayoutParams() {
- return (FrameLayout.LayoutParams) mView.getLayoutParams();
+ public ViewGroup.LayoutParams getLayoutParams() {
+ return mView.getLayoutParams();
}
- public void setLayoutParams(FrameLayout.LayoutParams lp) {
+ /**
+ * Updates layout parameters on the root view
+ */
+ public void setLayoutParams(ViewGroup.LayoutParams lp) {
mView.setLayoutParams(lp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index e1eaf3c..f289b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -71,7 +71,7 @@
"persist.sysui.wake_performs_auth", true);
private boolean mDozingRequested;
private boolean mPulsing;
- private WakefulnessLifecycle mWakefulnessLifecycle;
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
private final SysuiStatusBarStateController mStatusBarStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
@@ -86,9 +86,8 @@
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
- private final LockscreenLockIconController mLockscreenLockIconController;
private final AuthController mAuthController;
- private NotificationIconAreaController mNotificationIconAreaController;
+ private final NotificationIconAreaController mNotificationIconAreaController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private NotificationPanelViewController mNotificationPanel;
private View mAmbientIndicationContainer;
@@ -109,7 +108,6 @@
PulseExpansionHandler pulseExpansionHandler,
NotificationShadeWindowController notificationShadeWindowController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- LockscreenLockIconController lockscreenLockIconController,
AuthController authController,
NotificationIconAreaController notificationIconAreaController) {
super();
@@ -129,7 +127,6 @@
mPulseExpansionHandler = pulseExpansionHandler;
mNotificationShadeWindowController = notificationShadeWindowController;
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
- mLockscreenLockIconController = lockscreenLockIconController;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index eceac32..7011eee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -37,12 +37,10 @@
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -52,18 +50,20 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.ViewController;
import java.util.Optional;
import javax.inject.Inject;
-/** Controls the {@link LockIcon} in the lockscreen. */
-@SysUISingleton
-public class LockscreenLockIconController {
+/** Controls the {@link LockIcon} on the lockscreen. */
+@StatusBarComponent.StatusBarScope
+public class LockscreenLockIconController extends ViewController<LockIcon> {
private final LockscreenGestureLogger mLockscreenGestureLogger;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -79,7 +79,6 @@
private final KeyguardStateController mKeyguardStateController;
private final Resources mResources;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
- private final KeyguardSecurityModel mKeyguardSecurityModel;
private boolean mKeyguardShowing;
private boolean mKeyguardJustShown;
private boolean mBlockUpdates;
@@ -92,50 +91,308 @@
private boolean mBouncerShowingScrimmed;
private boolean mFingerprintUnlock;
private int mStatusBarState = StatusBarState.SHADE;
- private LockIcon mLockIcon;
+ private int mLastState;
+ private boolean mDozing;
- private View.OnAttachStateChangeListener mOnAttachStateChangeListener =
- new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- mStatusBarStateController.addCallback(mSBStateListener);
- mConfigurationController.addCallback(mConfigurationListener);
- mNotificationWakeUpCoordinator.addListener(mWakeUpListener);
- mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
- mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
+ @Inject
+ public LockscreenLockIconController(
+ @Nullable LockIcon view,
+ LockscreenGestureLogger lockscreenGestureLogger,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ LockPatternUtils lockPatternUtils,
+ ShadeController shadeController,
+ AccessibilityController accessibilityController,
+ KeyguardIndicationController keyguardIndicationController,
+ StatusBarStateController statusBarStateController,
+ ConfigurationController configurationController,
+ NotificationWakeUpCoordinator notificationWakeUpCoordinator,
+ KeyguardBypassController keyguardBypassController,
+ @Nullable DockManager dockManager,
+ KeyguardStateController keyguardStateController,
+ @Main Resources resources,
+ HeadsUpManagerPhone headsUpManagerPhone) {
+ super(view);
+ mLockscreenGestureLogger = lockscreenGestureLogger;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mShadeController = shadeController;
+ mAccessibilityController = accessibilityController;
+ mKeyguardIndicationController = keyguardIndicationController;
+ mStatusBarStateController = statusBarStateController;
+ mConfigurationController = configurationController;
+ mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
+ mKeyguardBypassController = keyguardBypassController;
+ mDockManager = dockManager == null ? Optional.empty() : Optional.of(dockManager);
+ mKeyguardStateController = keyguardStateController;
+ mResources = resources;
+ mHeadsUpManagerPhone = headsUpManagerPhone;
- mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener));
+ if (view != null) {
+ mKeyguardIndicationController.setLockIconController(this);
+ }
+ }
- mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
- mConfigurationListener.onThemeChanged();
+ @Override
+ protected void onInit() {
+ if (mView == null) {
+ return;
+ }
+ mView.setOnClickListener(this::handleClick);
+ mView.setOnLongClickListener(this::handleLongClick);
+ mView.setAccessibilityDelegate(mAccessibilityDelegate);
+ }
- updateColor();
+ @Override
+ protected void onViewAttached() {
+ setStatusBarState(mStatusBarStateController.getState());
+ mDozing = mStatusBarStateController.isDozing();
+ mStatusBarStateController.addCallback(mSBStateListener);
+ mConfigurationController.addCallback(mConfigurationListener);
+ mNotificationWakeUpCoordinator.addListener(mWakeUpListener);
+ mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
+
+ mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener));
+
+ mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
+ mConfigurationListener.onThemeChanged();
+
+ updateColor();
+ update();
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mStatusBarStateController.removeCallback(mSBStateListener);
+ mConfigurationController.removeCallback(mConfigurationListener);
+ mNotificationWakeUpCoordinator.removeListener(mWakeUpListener);
+ mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+ mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
+
+ mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener));
+ }
+
+ /**
+ * Called whenever the scrims become opaque, transparent or semi-transparent.
+ */
+ public void onScrimVisibilityChanged(Integer scrimsVisible) {
+ if (mWakeAndUnlockRunning
+ && scrimsVisible == ScrimController.TRANSPARENT) {
+ mWakeAndUnlockRunning = false;
update();
}
+ }
- @Override
- public void onViewDetachedFromWindow(View v) {
- mStatusBarStateController.removeCallback(mSBStateListener);
- mConfigurationController.removeCallback(mConfigurationListener);
- mNotificationWakeUpCoordinator.removeListener(mWakeUpListener);
- mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
- mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
-
- mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener));
+ /**
+ * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
+ * icon on top of the black front scrim.
+ * We also want to halt padlock the animation when we're in face bypass mode or dismissing the
+ * keyguard with fingerprint.
+ * @param wakeAndUnlock are we wake and unlocking
+ * @param isUnlock are we currently unlocking
+ */
+ public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock,
+ BiometricSourceType type) {
+ if (wakeAndUnlock) {
+ mWakeAndUnlockRunning = true;
}
- };
+ mFingerprintUnlock = type == BiometricSourceType.FINGERPRINT;
+ if (isUnlock && (mFingerprintUnlock || mKeyguardBypassController.getBypassEnabled())
+ && canBlockUpdates()) {
+ // We don't want the icon to change while we are unlocking
+ mBlockUpdates = true;
+ }
+ update();
+ }
+
+ /**
+ * When we're launching an affordance, like double pressing power to open camera.
+ */
+ public void onShowingLaunchAffordanceChanged(Boolean showing) {
+ mShowingLaunchAffordance = showing;
+ update();
+ }
+
+ /** Sets whether the bouncer is showing. */
+ public void setBouncerShowingScrimmed(boolean showing, boolean scrimmed) {
+ mBouncerShowingScrimmed = scrimmed;
+ update();
+ }
+
+ /**
+ * Sets how hidden the bouncer is, where 0f is fully visible and 1f is fully hidden
+ * See {@link KeyguardBouncer#EXPANSION_VISIBLE} and {@link KeyguardBouncer#EXPANSION_HIDDEN}.
+ */
+ public void setBouncerHideAmount(float hideAmount) {
+ mBouncerHiddenAmount = hideAmount;
+ updateColor();
+ }
+
+ private void updateColor() {
+ if (mView == null) {
+ return;
+ }
+ int iconColor = -1;
+ if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_VISIBLE) {
+ TypedArray typedArray = mView.getContext().getTheme().obtainStyledAttributes(
+ null, new int[]{ android.R.attr.textColorPrimary }, 0, 0);
+ iconColor = typedArray.getColor(0, Color.WHITE);
+ typedArray.recycle();
+ } else if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_HIDDEN) {
+ iconColor = Utils.getColorAttrDefaultColor(
+ mView.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
+ } else {
+ // bouncer is transitioning
+ TypedArray typedArray = mView.getContext().getTheme().obtainStyledAttributes(
+ null, new int[]{ android.R.attr.textColorPrimary }, 0, 0);
+ int bouncerIconColor = typedArray.getColor(0, Color.WHITE);
+ typedArray.recycle();
+ int keyguardIconColor = Utils.getColorAttrDefaultColor(
+ mView.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
+ iconColor = (int) new ArgbEvaluator().evaluate(
+ mBouncerHiddenAmount, bouncerIconColor, keyguardIconColor);
+ }
+ mView.updateColor(iconColor);
+ }
+
+ /**
+ * Animate padlock opening when bouncer challenge is solved.
+ */
+ public void onBouncerPreHideAnimation() {
+ update();
+ }
+
+ /**
+ * If we're currently presenting an authentication error message.
+ */
+ public void setTransientBiometricsError(boolean transientBiometricsError) {
+ mTransientBiometricsError = transientBiometricsError;
+ update();
+ }
+
+ private boolean handleLongClick(View view) {
+ mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK,
+ 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+ mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_LOCK_TAP);
+ mKeyguardIndicationController.showTransientIndication(
+ R.string.keyguard_indication_trust_disabled);
+ mKeyguardUpdateMonitor.onLockIconPressed();
+ mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
+
+ return true;
+ }
+
+
+ private void handleClick(View view) {
+ if (!mAccessibilityController.isAccessibilityEnabled()) {
+ return;
+ }
+ mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+ }
+
+ private void update() {
+ update(false /* force */);
+ }
+
+ private void update(boolean force) {
+ if (mView == null) {
+ return;
+ }
+ int state = getState();
+ boolean shouldUpdate = mLastState != state || force;
+ if (mBlockUpdates && canBlockUpdates()) {
+ shouldUpdate = false;
+ }
+ if (shouldUpdate && mView.getVisibility() != GONE) {
+ mView.update(state, mDozing, mKeyguardJustShown);
+ }
+ mLastState = state;
+ mKeyguardJustShown = false;
+ updateIconVisibility();
+ updateClickability();
+ }
+
+ private int getState() {
+ if ((mKeyguardStateController.canDismissLockScreen()
+ || !mKeyguardStateController.isShowing()
+ || mKeyguardStateController.isKeyguardGoingAway()
+ || mKeyguardStateController.isKeyguardFadingAway()) && !mSimLocked) {
+ return STATE_LOCK_OPEN;
+ } else if (mTransientBiometricsError) {
+ return STATE_BIOMETRICS_ERROR;
+ } else if (mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
+ return STATE_SCANNING_FACE;
+ } else {
+ return STATE_LOCKED;
+ }
+ }
+
+ private boolean canBlockUpdates() {
+ return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
+ }
+
+ /** Set the StatusBarState. */
+ private void setStatusBarState(int statusBarState) {
+ mStatusBarState = statusBarState;
+ updateIconVisibility();
+ }
+
+ /**
+ * Update the icon visibility
+ * @return true if the visibility changed
+ */
+ private boolean updateIconVisibility() {
+ if (mView == null) {
+ return false;
+ }
+ if (!mKeyguardUpdateMonitor.canShowLockIcon()) {
+ boolean changed = mView.getVisibility() != GONE;
+ mView.setVisibility(GONE);
+ return changed;
+ }
+
+ boolean onAodOrDocked = mDozing || mDocked;
+ boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance;
+ boolean fingerprintOrBypass = mFingerprintUnlock
+ || mKeyguardBypassController.getBypassEnabled();
+ if (fingerprintOrBypass && !mBouncerShowingScrimmed) {
+ if ((mHeadsUpManagerPhone.isHeadsUpGoingAway()
+ || mHeadsUpManagerPhone.hasPinnedHeadsUp()
+ || mStatusBarState == StatusBarState.KEYGUARD
+ || mStatusBarState == StatusBarState.SHADE)
+ && !mNotificationWakeUpCoordinator.getNotificationsFullyHidden()) {
+ invisible = true;
+ }
+ }
+ return mView.updateIconVisibility(!invisible);
+ }
+
+ private void updateClickability() {
+ if (mView == null) {
+ return;
+ }
+ boolean canLock = mKeyguardStateController.isMethodSecure()
+ && mKeyguardStateController.canDismissLockScreen();
+ boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
+ mView.setClickable(clickToUnlock);
+ mView.setLongClickable(canLock && !clickToUnlock);
+ mView.setFocusable(mAccessibilityController.isAccessibilityEnabled());
+ }
private final StatusBarStateController.StateListener mSBStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onDozingChanged(boolean isDozing) {
- setDozing(isDozing);
+ if (mDozing != isDozing) {
+ mDozing = isDozing;
+ update();
+ }
}
@Override
public void onDozeAmountChanged(float linear, float eased) {
- if (mLockIcon != null) {
- mLockIcon.setDozeAmount(eased);
+ if (mView != null) {
+ mView.setDozeAmount(eased);
}
}
@@ -160,29 +417,21 @@
@Override
public void onDensityOrFontScaleChanged() {
- if (mLockIcon == null) {
- return;
- }
-
- ViewGroup.LayoutParams lp = mLockIcon.getLayoutParams();
+ ViewGroup.LayoutParams lp = mView.getLayoutParams();
if (lp == null) {
return;
}
- lp.width = mLockIcon.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_width);
- lp.height = mLockIcon.getResources().getDimensionPixelSize(
+ lp.width = mView.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_width);
+ lp.height = mView.getResources().getDimensionPixelSize(
R.dimen.keyguard_lock_height);
- mLockIcon.setLayoutParams(lp);
+ mView.setLayoutParams(lp);
update(true /* force */);
}
@Override
public void onLocaleListChanged() {
- if (mLockIcon == null) {
- return;
- }
-
- mLockIcon.setContentDescription(
- mLockIcon.getResources().getText(R.string.accessibility_unlock_button));
+ mView.setContentDescription(
+ mView.getResources().getText(R.string.accessibility_unlock_button));
update(true /* force */);
}
@@ -299,9 +548,9 @@
if (fingerprintRunning && unlockingAllowed) {
AccessibilityNodeInfo.AccessibilityAction unlock =
new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLICK,
- mResources.getString(
- R.string.accessibility_unlock_without_fingerprint));
+ AccessibilityNodeInfo.ACTION_CLICK,
+ mResources.getString(
+ R.string.accessibility_unlock_without_fingerprint));
info.addAction(unlock);
info.setHintText(mResources.getString(
R.string.accessibility_waiting_for_fingerprint));
@@ -313,277 +562,4 @@
}
}
};
- private int mLastState;
-
- @Inject
- public LockscreenLockIconController(LockscreenGestureLogger lockscreenGestureLogger,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- LockPatternUtils lockPatternUtils,
- ShadeController shadeController,
- AccessibilityController accessibilityController,
- KeyguardIndicationController keyguardIndicationController,
- StatusBarStateController statusBarStateController,
- ConfigurationController configurationController,
- NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- KeyguardBypassController keyguardBypassController,
- @Nullable DockManager dockManager,
- KeyguardStateController keyguardStateController,
- @Main Resources resources,
- HeadsUpManagerPhone headsUpManagerPhone,
- KeyguardSecurityModel keyguardSecurityModel) {
- mLockscreenGestureLogger = lockscreenGestureLogger;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mLockPatternUtils = lockPatternUtils;
- mShadeController = shadeController;
- mAccessibilityController = accessibilityController;
- mKeyguardIndicationController = keyguardIndicationController;
- mStatusBarStateController = statusBarStateController;
- mConfigurationController = configurationController;
- mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
- mKeyguardBypassController = keyguardBypassController;
- mDockManager = dockManager == null ? Optional.empty() : Optional.of(dockManager);
- mKeyguardStateController = keyguardStateController;
- mResources = resources;
- mHeadsUpManagerPhone = headsUpManagerPhone;
- mKeyguardSecurityModel = keyguardSecurityModel;
-
- mKeyguardIndicationController.setLockIconController(this);
- }
-
- /**
- * Associate the controller with a {@link LockIcon}
- *
- * TODO: change to an init method and inject the view.
- */
- public void attach(LockIcon lockIcon) {
- mLockIcon = lockIcon;
-
- mLockIcon.setOnClickListener(this::handleClick);
- mLockIcon.setOnLongClickListener(this::handleLongClick);
- mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
-
- if (mLockIcon.isAttachedToWindow()) {
- mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
- }
- mLockIcon.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
- setStatusBarState(mStatusBarStateController.getState());
- }
-
- public LockIcon getView() {
- return mLockIcon;
- }
-
- /**
- * Called whenever the scrims become opaque, transparent or semi-transparent.
- */
- public void onScrimVisibilityChanged(Integer scrimsVisible) {
- if (mWakeAndUnlockRunning
- && scrimsVisible == ScrimController.TRANSPARENT) {
- mWakeAndUnlockRunning = false;
- update();
- }
- }
-
- /**
- * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
- * icon on top of the black front scrim.
- * We also want to halt padlock the animation when we're in face bypass mode or dismissing the
- * keyguard with fingerprint.
- * @param wakeAndUnlock are we wake and unlocking
- * @param isUnlock are we currently unlocking
- */
- public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock,
- BiometricSourceType type) {
- if (wakeAndUnlock) {
- mWakeAndUnlockRunning = true;
- }
- mFingerprintUnlock = type == BiometricSourceType.FINGERPRINT;
- if (isUnlock && (mFingerprintUnlock || mKeyguardBypassController.getBypassEnabled())
- && canBlockUpdates()) {
- // We don't want the icon to change while we are unlocking
- mBlockUpdates = true;
- }
- update();
- }
-
- /**
- * When we're launching an affordance, like double pressing power to open camera.
- */
- public void onShowingLaunchAffordanceChanged(Boolean showing) {
- mShowingLaunchAffordance = showing;
- update();
- }
-
- /** Sets whether the bouncer is showing. */
- public void setBouncerShowingScrimmed(boolean showing, boolean scrimmed) {
- mBouncerShowingScrimmed = scrimmed;
- update();
- }
-
- /**
- * Sets how hidden the bouncer is, where 0f is fully visible and 1f is fully hidden
- * See {@link KeyguardBouncer#EXPANSION_VISIBLE} and {@link KeyguardBouncer#EXPANSION_HIDDEN}.
- */
- public void setBouncerHideAmount(float hideAmount) {
- mBouncerHiddenAmount = hideAmount;
- updateColor();
- }
-
- private void updateColor() {
- if (mLockIcon == null) {
- return;
- }
-
- int iconColor = -1;
- if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_VISIBLE) {
- TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
- null, new int[]{ android.R.attr.textColorPrimary }, 0, 0);
- iconColor = typedArray.getColor(0, Color.WHITE);
- typedArray.recycle();
- } else if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_HIDDEN) {
- iconColor = Utils.getColorAttrDefaultColor(
- mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
- } else {
- // bouncer is transitioning
- TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
- null, new int[]{ android.R.attr.textColorPrimary }, 0, 0);
- int bouncerIconColor = typedArray.getColor(0, Color.WHITE);
- typedArray.recycle();
- int keyguardIconColor = Utils.getColorAttrDefaultColor(
- mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
- iconColor = (int) new ArgbEvaluator().evaluate(
- mBouncerHiddenAmount, bouncerIconColor, keyguardIconColor);
- }
- mLockIcon.updateColor(iconColor);
- }
-
- /**
- * Animate padlock opening when bouncer challenge is solved.
- */
- public void onBouncerPreHideAnimation() {
- update();
- }
-
- /**
- * If we're currently presenting an authentication error message.
- */
- public void setTransientBiometricsError(boolean transientBiometricsError) {
- mTransientBiometricsError = transientBiometricsError;
- update();
- }
-
- private boolean handleLongClick(View view) {
- mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK,
- 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
- mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_LOCK_TAP);
- mKeyguardIndicationController.showTransientIndication(
- R.string.keyguard_indication_trust_disabled);
- mKeyguardUpdateMonitor.onLockIconPressed();
- mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
-
- return true;
- }
-
-
- private void handleClick(View view) {
- if (!mAccessibilityController.isAccessibilityEnabled()) {
- return;
- }
- mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
- }
-
- private void update() {
- update(false /* force */);
- }
-
- private void update(boolean force) {
- int state = getState();
- boolean shouldUpdate = mLastState != state || force;
- if (mBlockUpdates && canBlockUpdates()) {
- shouldUpdate = false;
- }
- if (shouldUpdate && mLockIcon != null && mLockIcon.getVisibility() != GONE) {
- mLockIcon.update(state,
- mStatusBarStateController.isDozing(), mKeyguardJustShown);
- }
- mLastState = state;
- mKeyguardJustShown = false;
- updateIconVisibility();
- updateClickability();
- }
-
- private int getState() {
- if ((mKeyguardStateController.canDismissLockScreen()
- || !mKeyguardStateController.isShowing()
- || mKeyguardStateController.isKeyguardGoingAway()
- || mKeyguardStateController.isKeyguardFadingAway()) && !mSimLocked) {
- return STATE_LOCK_OPEN;
- } else if (mTransientBiometricsError) {
- return STATE_BIOMETRICS_ERROR;
- } else if (mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
- return STATE_SCANNING_FACE;
- } else {
- return STATE_LOCKED;
- }
- }
-
- private boolean canBlockUpdates() {
- return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
- }
-
- private void setDozing(boolean isDozing) {
- update();
- }
-
- /** Set the StatusBarState. */
- private void setStatusBarState(int statusBarState) {
- mStatusBarState = statusBarState;
- updateIconVisibility();
- }
-
- /**
- * Update the icon visibility
- * @return true if the visibility changed
- */
- private boolean updateIconVisibility() {
- if (mLockIcon == null) {
- return false;
- }
-
- if (!mKeyguardUpdateMonitor.shouldShowLockIcon()) {
- boolean changed = mLockIcon.getVisibility() != GONE;
- mLockIcon.setVisibility(GONE);
- return changed;
- }
-
- boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked;
- boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance;
- boolean fingerprintOrBypass = mFingerprintUnlock
- || mKeyguardBypassController.getBypassEnabled();
- if (fingerprintOrBypass && !mBouncerShowingScrimmed) {
- if ((mHeadsUpManagerPhone.isHeadsUpGoingAway()
- || mHeadsUpManagerPhone.hasPinnedHeadsUp()
- || mStatusBarState == StatusBarState.KEYGUARD
- || mStatusBarState == StatusBarState.SHADE)
- && !mNotificationWakeUpCoordinator.getNotificationsFullyHidden()) {
- invisible = true;
- }
- }
- return mLockIcon.updateIconVisibility(!invisible);
- }
-
- private void updateClickability() {
- if (mAccessibilityController == null) {
- return;
- }
- boolean canLock = mKeyguardStateController.isMethodSecure()
- && mKeyguardStateController.canDismissLockScreen();
- boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
- if (mLockIcon != null) {
- mLockIcon.setClickable(clickToUnlock);
- mLockIcon.setLongClickable(canLock && !clickToUnlock);
- mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled());
- }
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index b24d0e7..a30f193 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -120,6 +120,7 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -552,10 +553,12 @@
AuthController authController,
QSDetailDisplayer qsDetailDisplayer,
ScrimController scrimController,
- MediaDataManager mediaDataManager) {
+ MediaDataManager mediaDataManager,
+ AmbientState ambientState) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
- latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager);
+ latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
+ ambientState);
mView = view;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
@@ -753,19 +756,16 @@
public void updateResources() {
int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
- int panelGravity = mResources.getInteger(R.integer.notification_panel_layout_gravity);
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
- if (lp.width != qsWidth || lp.gravity != panelGravity) {
+ ViewGroup.LayoutParams lp = mQsFrame.getLayoutParams();
+ if (lp.width != qsWidth) {
lp.width = qsWidth;
- lp.gravity = panelGravity;
mQsFrame.setLayoutParams(lp);
}
int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
lp = mNotificationStackScrollLayoutController.getLayoutParams();
- if (lp.width != panelWidth || lp.gravity != panelGravity) {
+ if (lp.width != panelWidth) {
lp.width = panelWidth;
- lp.gravity = panelGravity;
mNotificationStackScrollLayoutController.setLayoutParams(lp);
}
}
@@ -910,7 +910,7 @@
clockPreferredY, hasCustomClock(),
hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
- mUpdateMonitor.shouldShowLockIcon(),
+ mUpdateMonitor.canShowLockIcon(),
getQsExpansionFraction(),
mDisplayCutoutTopInset);
mClockPositionAlgorithm.run(mClockPositionResult);
@@ -2411,11 +2411,6 @@
}
@Override
- protected void setIsShadeOpening(boolean isOpening) {
- mNotificationStackScrollLayoutController.setIsShadeOpening(isOpening);
- }
-
- @Override
public void setSectionPadding(float padding) {
if (padding == mSectionPadding) {
return;
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 27c94d2..b367406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -28,6 +28,7 @@
import android.widget.FrameLayout;
import androidx.annotation.DimenRes;
+import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.systemui.R;
import com.android.systemui.fragments.FragmentHostManager;
@@ -42,7 +43,7 @@
/**
* The container with notification stack scroller and quick settings inside.
*/
-public class NotificationsQuickSettingsContainer extends FrameLayout
+public class NotificationsQuickSettingsContainer extends ConstraintLayout
implements OnInflateListener, FragmentListener,
AboveShelfObserver.HasViewAboveShelfChangedListener {
@@ -68,11 +69,11 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mQsFrame = (FrameLayout) findViewById(R.id.qs_frame);
+ mQsFrame = findViewById(R.id.qs_frame);
mStackScroller = findViewById(R.id.notification_stack_scroller);
mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
- ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher);
+ ViewStub userSwitcher = findViewById(R.id.keyguard_user_switcher);
userSwitcher.setOnInflateListener(this);
mUserSwitcher = userSwitcher;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index bee0cb1..28cfe21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -53,7 +53,7 @@
if (DEBUG) LOG("go state: %d -> %d", mState, state);
mState = state;
if (mPanel != null) {
- mPanel.setIsShadeOpening(state == STATE_OPENING);
+ mPanel.getAmbientState().setIsShadeOpening(state == STATE_OPENING);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 3031b8a..55744f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -27,6 +27,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.SystemClock;
@@ -54,6 +55,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -145,6 +147,9 @@
private float mInitialTouchX;
private boolean mTouchDisabled;
+ // AmbientState will never be null since it provides an @Inject constructor for Dagger to call.
+ private AmbientState mAmbientState;
+
/**
* Whether or not the PanelView can be expanded or collapsed with a drag.
*/
@@ -223,13 +228,19 @@
mJustPeeked = true;
}
+ protected AmbientState getAmbientState() {
+ return mAmbientState;
+ }
+
public PanelViewController(PanelView view,
FalsingManager falsingManager, DozeLog dozeLog,
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
LatencyTracker latencyTracker,
FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+ AmbientState ambientState) {
+ mAmbientState = ambientState;
mView = view;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
@@ -776,8 +787,6 @@
*/
protected abstract boolean isTrackingBlocked();
- protected abstract void setIsShadeOpening(boolean isShadeOpening);
-
protected abstract void setSectionPadding(float padding);
protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index c0a5ffa..31a432e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.leak.RotationUtils;
+import java.util.List;
import java.util.Objects;
public class PhoneStatusBarView extends PanelBar {
@@ -75,6 +76,8 @@
@Nullable
private DisplayCutout mDisplayCutout;
private int mStatusBarHeight;
+ @Nullable
+ private List<StatusBar.ExpansionChangedListener> mExpansionChangedListeners;
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -93,6 +96,11 @@
mBar = bar;
}
+ public void setExpansionChangedListeners(
+ @Nullable List<StatusBar.ExpansionChangedListener> listeners) {
+ mExpansionChangedListeners = listeners;
+ }
+
public void setScrimController(ScrimController scrimController) {
mScrimController = scrimController;
}
@@ -277,6 +285,12 @@
if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
mBar.getNavigationBarView().onStatusBarPanelStateChanged();
}
+
+ if (mExpansionChangedListeners != null) {
+ for (StatusBar.ExpansionChangedListener listener : mExpansionChangedListeners) {
+ listener.onExpansionChanged(frac, expanded);
+ }
+ }
}
private void updateScrimFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 19c0b6d..7b2330b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -34,7 +34,6 @@
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -48,6 +47,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.FeatureFlags;
@@ -63,8 +63,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -150,6 +149,7 @@
private final AlarmTimeout mTimeTicker;
private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
private final Handler mHandler;
+ private final Executor mMainExecutor;
private final BlurUtils mBlurUtils;
private GradientColors mColors;
@@ -198,36 +198,25 @@
private boolean mWakeLockHeld;
private boolean mKeyguardOccluded;
- /**
- * Notifies listeners of animation-related changes (currently just opacity changes).
- */
- public interface ScrimChangedListener {
- void onAlphaChanged(float alpha);
- }
-
- @NonNull
- private final List<ScrimChangedListener> mScrimChangedListeners;
-
@Inject
public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters,
AlarmManager alarmManager, KeyguardStateController keyguardStateController,
DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager,
BlurUtils blurUtils, ConfigurationController configurationController,
- FeatureFlags featureFlags) {
-
+ FeatureFlags featureFlags, @Main Executor mainExecutor) {
mScrimStateListener = lightBarController::setScrimState;
mDefaultScrimAlpha = featureFlags.isShadeOpaque() ? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA;
ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(featureFlags.isShadeOpaque()
? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA);
mBlurUtils = blurUtils;
- mScrimChangedListeners = new ArrayList<>();
mKeyguardStateController = keyguardStateController;
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
mHandler = handler;
+ mMainExecutor = mainExecutor;
mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
"hide_aod_wallpaper", mHandler);
mWakeLock = delayedWakeLockBuilder.setHandler(mHandler).setTag("Scrims").build();
@@ -273,7 +262,7 @@
updateThemeColors();
if (mScrimBehindChangeRunnable != null) {
- mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable);
+ mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable, mMainExecutor);
mScrimBehindChangeRunnable = null;
}
@@ -298,10 +287,6 @@
mScrimVisibleListener = listener;
}
- public void addScrimChangedListener(@NonNull ScrimChangedListener listener) {
- mScrimChangedListeners.add(listener);
- }
-
public void transitionTo(ScrimState state) {
transitionTo(state, null);
}
@@ -577,10 +562,6 @@
throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
+ ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha);
}
-
- for (ScrimChangedListener listener : mScrimChangedListeners) {
- listener.onAlphaChanged(mBehindAlpha);
- }
}
private void applyAndDispatchExpansion() {
@@ -1022,7 +1003,7 @@
if (mScrimBehind == null) {
mScrimBehindChangeRunnable = changeRunnable;
} else {
- mScrimBehind.setChangeRunnable(changeRunnable);
+ mScrimBehind.setChangeRunnable(changeRunnable, mMainExecutor);
}
}
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 b20c457..7095afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -26,6 +26,7 @@
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
import static androidx.lifecycle.Lifecycle.State.RESUMED;
@@ -337,6 +338,10 @@
ONLY_CORE_APPS = onlyCoreApps;
}
+ public interface ExpansionChangedListener {
+ void onExpansionChanged(float expansion, boolean expanded);
+ }
+
/**
* The {@link StatusBarState} of the status bar.
*/
@@ -367,7 +372,6 @@
protected NotificationShadeWindowController mNotificationShadeWindowController;
protected StatusBarWindowController mStatusBarWindowController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final LockscreenLockIconController mLockscreenLockIconController;
@VisibleForTesting
DozeServiceHost mDozeServiceHost;
private boolean mWakeUpComingFromTouch;
@@ -414,6 +418,7 @@
// expanded notifications
// the sliding/resizing panel within the notification window
protected NotificationPanelViewController mNotificationPanelViewController;
+ protected LockscreenLockIconController mLockscreenLockIconController;
// settings
private QSPanelController mQSPanelController;
@@ -438,6 +443,8 @@
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSlider.Factory mBrightnessSliderFactory;
+ private final List<ExpansionChangedListener> mExpansionChangedListeners;
+
// for disabling the status bar
private int mDisabled1 = 0;
private int mDisabled2 = 0;
@@ -724,7 +731,6 @@
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
DozeParameters dozeParameters,
ScrimController scrimController,
@Nullable KeyguardLiftController keyguardLiftController,
@@ -806,7 +812,6 @@
mAssistManagerLazy = assistManagerLazy;
mConfigurationController = configurationController;
mNotificationShadeWindowController = notificationShadeWindowController;
- mLockscreenLockIconController = lockscreenLockIconController;
mDozeServiceHost = dozeServiceHost;
mPowerManager = powerManager;
mDozeParameters = dozeParameters;
@@ -839,10 +844,14 @@
mNotificationIconAreaController = notificationIconAreaController;
mBrightnessSliderFactory = brightnessSliderFactory;
+ mExpansionChangedListeners = new ArrayList<>();
+
mBubbleExpandListener =
(isExpanding, key) -> {
- mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
- updateScrimController();
+ mContext.getMainExecutor().execute(() -> {
+ mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
+ updateScrimController();
+ });
};
mActivityIntentHelper = new ActivityIntentHelper(mContext);
@@ -1076,6 +1085,7 @@
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanelViewController);
mStatusBarView.setScrimController(mScrimController);
+ mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
// CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
@@ -1170,9 +1180,7 @@
mScrimController.setScrimVisibleListener(scrimsVisible -> {
mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
- if (mNotificationShadeWindowView != null) {
- mLockscreenLockIconController.onScrimVisibilityChanged(scrimsVisible);
- }
+ mLockscreenLockIconController.onScrimVisibilityChanged(scrimsVisible);
});
mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble);
@@ -1205,9 +1213,6 @@
createUserSwitcher();
}
- mNotificationPanelViewController.setLaunchAffordanceListener(
- mLockscreenLockIconController::onShowingLaunchAffordanceChanged);
-
// Set up the quick settings tile panel
final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
if (container != null) {
@@ -1487,6 +1492,11 @@
mStatusBarWindowController = statusBarComponent.getStatusBarWindowController();
mPhoneStatusBarWindow = mSuperStatusBarViewFactory.getStatusBarWindowView();
mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
+ mLockscreenLockIconController = statusBarComponent.getLockscreenLockIconController();
+ mLockscreenLockIconController.init();
+
+ mNotificationPanelViewController.setLaunchAffordanceListener(
+ mLockscreenLockIconController::onShowingLaunchAffordanceChanged);
}
protected void startKeyguard() {
@@ -2428,6 +2438,8 @@
return MODE_LIGHTS_OUT_TRANSPARENT;
} else if ((appearance & APPEARANCE_OPAQUE_STATUS_BARS) != 0) {
return MODE_OPAQUE;
+ } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS) != 0) {
+ return MODE_SEMI_TRANSPARENT;
} else {
return MODE_TRANSPARENT;
}
@@ -4547,4 +4559,8 @@
public void suppressAmbientDisplay(boolean suppressed) {
mDozeServiceHost.setDozeSuppressed(suppressed);
}
+
+ public void addExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
+ mExpansionChangedListeners.add(listener);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 5f90077..f6165f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -316,11 +316,23 @@
mIconController.removeAllIconsForSlot(mSlotMobile);
mMobileStates.clear();
+ List<NoCallingIconState> noCallingStates = new ArrayList<NoCallingIconState>();
+ noCallingStates.addAll(mNoCallingStates);
mNoCallingStates.clear();
final int n = subs.size();
for (int i = 0; i < n; i++) {
mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
- mNoCallingStates.add(new NoCallingIconState(subs.get(i).getSubscriptionId()));
+ boolean isNewSub = true;
+ for (NoCallingIconState state : noCallingStates) {
+ if (state.subId == subs.get(i).getSubscriptionId()) {
+ mNoCallingStates.add(state);
+ isNewSub = false;
+ break;
+ }
+ }
+ if (isNewSub) {
+ mNoCallingStates.add(new NoCallingIconState(subs.get(i).getSubscriptionId()));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index 802da3e..ecd9613 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -18,6 +18,7 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
@@ -73,4 +74,9 @@
@StatusBarScope
NotificationPanelViewController getNotificationPanelViewController();
+ /**
+ * Creates a LockscreenLockIconController.
+ */
+ @StatusBarScope
+ LockscreenLockIconController getLockscreenLockIconController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 26e1959..9e9533d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -77,7 +77,6 @@
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
@@ -167,7 +166,6 @@
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
DozeParameters dozeParameters,
ScrimController scrimController,
@Nullable KeyguardLiftController keyguardLiftController,
@@ -248,7 +246,6 @@
assistManagerLazy,
configurationController,
notificationShadeWindowController,
- lockscreenLockIconController,
dozeParameters,
scrimController,
keyguardLiftController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 37d8c9a..781abe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar.phone.dagger;
+import android.annotation.Nullable;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -32,4 +36,12 @@
return notificationShadeWindowView.getNotificationPanelView();
}
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ @Nullable
+ public static LockIcon getLockIcon(
+ NotificationShadeWindowView notificationShadeWindowView) {
+ return notificationShadeWindowView.findViewById(R.id.lock_icon);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
index 2eff04e..80b75a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
@@ -53,13 +53,21 @@
public void notifyListeners(SignalCallback callback) {
boolean ethernetVisible = mCurrentState.connected;
String contentDescription = getTextIfExists(getContentDescription()).toString();
-
// TODO: wire up data transfer using WifiSignalPoller.
callback.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(),
contentDescription));
}
@Override
+ public int getContentDescription() {
+ if (mCurrentState.connected) {
+ return getIcons().contentDesc[1];
+ } else {
+ return getIcons().discContentDesc;
+ }
+ }
+
+ @Override
public State cleanState() {
return new State();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f9450ae..e13e30b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -524,6 +524,10 @@
return mWifiSignalController.isCarrierMergedWifi(subId);
}
+ boolean isEthernetDefault() {
+ return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+ }
+
String getNonDefaultMobileDataNetworkName(int subId) {
MobileSignalController controller = getControllerWithSubId(subId);
return controller != null ? controller.getNonDefaultCarrierName() : "";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 9669522..b2120d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -106,7 +106,8 @@
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
if (mProviderModel) {
IconState qsIcon = null;
- if (mCurrentState.isDefault) {
+ if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
+ && !mNetworkController.isEthernetDefault())) {
qsIcon = new IconState(mCurrentState.connected,
mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
: getQsCurrentIconId(), contentDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index bf823b4..6e7aed0 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -38,6 +38,7 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -83,10 +84,14 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.function.IntConsumer;
+import java.util.function.Supplier;
/**
* The SysUi side bubbles manager which communicate with other SysUi components.
@@ -106,8 +111,9 @@
private final NotificationGroupManagerLegacy mNotificationGroupManager;
private final NotificationEntryManager mNotificationEntryManager;
private final NotifPipeline mNotifPipeline;
+ private final Executor mSysuiMainExecutor;
- private final ScrimView mBubbleScrim;
+ private ScrimView mBubbleScrim;
private final Bubbles.SysuiProxy mSysuiProxy;
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
private final List<NotifCallback> mCallbacks = new ArrayList<>();
@@ -133,14 +139,15 @@
NotifPipeline notifPipeline,
SysUiState sysUiState,
FeatureFlags featureFlags,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ Executor sysuiMainExecutor) {
if (bubblesOptional.isPresent()) {
return new BubblesManager(context, bubblesOptional.get(),
notificationShadeWindowController, statusBarStateController, shadeController,
configurationController, statusBarService, notificationManager,
interruptionStateProvider, zenModeController, notifUserManager,
groupManager, entryManager, notifPipeline, sysUiState, featureFlags,
- dumpManager);
+ dumpManager, sysuiMainExecutor);
} else {
return null;
}
@@ -163,7 +170,8 @@
NotifPipeline notifPipeline,
SysUiState sysUiState,
FeatureFlags featureFlags,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ Executor sysuiMainExecutor) {
mContext = context;
mBubbles = bubbles;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -173,6 +181,7 @@
mNotificationGroupManager = groupManager;
mNotificationEntryManager = entryManager;
mNotifPipeline = notifPipeline;
+ mSysuiMainExecutor = sysuiMainExecutor;
mBarService = statusBarService == null
? IStatusBarService.Stub.asInterface(
@@ -181,7 +190,9 @@
mBubbleScrim = new ScrimView(mContext);
mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- mBubbles.setBubbleScrim(mBubbleScrim);
+ mBubbles.setBubbleScrim(mBubbleScrim, (executor, looper) -> {
+ mBubbleScrim.setExecutor(executor, looper);
+ });
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
setupNotifPipeline();
@@ -237,128 +248,177 @@
});
mSysuiProxy = new Bubbles.SysuiProxy() {
+ private <T> T executeBlockingForResult(Supplier<T> runnable, Executor executor,
+ Class clazz) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ return runnable.get();
+ }
+ final T[] result = (T[]) Array.newInstance(clazz, 1);
+ final CountDownLatch latch = new CountDownLatch(1);
+ executor.execute(() -> {
+ result[0] = runnable.get();
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ return result[0];
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+
@Override
@Nullable
public BubbleEntry getPendingOrActiveEntry(String key) {
- NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
- return entry == null ? null : notifToBubbleEntry(entry);
+ return executeBlockingForResult(() -> {
+ NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ return entry == null ? null : notifToBubbleEntry(entry);
+ }, sysuiMainExecutor, BubbleEntry.class);
}
@Override
public List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys) {
- List<BubbleEntry> result = new ArrayList<>();
- List<NotificationEntry> activeEntries =
- mNotificationEntryManager.getActiveNotificationsForCurrentUser();
- for (int i = 0; i < activeEntries.size(); i++) {
- NotificationEntry entry = activeEntries.get(i);
- if (savedBubbleKeys.contains(entry.getKey())
- && mNotificationInterruptStateProvider.shouldBubbleUp(entry)
- && entry.isBubble()) {
- result.add(notifToBubbleEntry(entry));
+ return executeBlockingForResult(() -> {
+ List<BubbleEntry> result = new ArrayList<>();
+ List<NotificationEntry> activeEntries =
+ mNotificationEntryManager.getActiveNotificationsForCurrentUser();
+ for (int i = 0; i < activeEntries.size(); i++) {
+ NotificationEntry entry = activeEntries.get(i);
+ if (savedBubbleKeys.contains(entry.getKey())
+ && mNotificationInterruptStateProvider.shouldBubbleUp(entry)
+ && entry.isBubble()) {
+ result.add(notifToBubbleEntry(entry));
+ }
}
- }
- return result;
+ return result;
+ }, sysuiMainExecutor, List.class);
}
@Override
public boolean isNotificationShadeExpand() {
- return mNotificationShadeWindowController.getPanelExpanded();
+ return executeBlockingForResult(() -> {
+ return mNotificationShadeWindowController.getPanelExpanded();
+ }, sysuiMainExecutor, Boolean.class);
}
@Override
public boolean shouldBubbleUp(String key) {
- final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- key);
- if (entry != null) {
- return mNotificationInterruptStateProvider.shouldBubbleUp(entry);
- }
- return false;
+ return executeBlockingForResult(() -> {
+ final NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ if (entry != null) {
+ return mNotificationInterruptStateProvider.shouldBubbleUp(entry);
+ }
+ return false;
+ }, sysuiMainExecutor, Boolean.class);
}
@Override
public void setNotificationInterruption(String key) {
- final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- key);
- if (entry != null && entry.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
- entry.setInterruption();
- }
+ sysuiMainExecutor.execute(() -> {
+ final NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ if (entry != null
+ && entry.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
+ entry.setInterruption();
+ }
+ });
}
@Override
public void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag) {
- mNotificationShadeWindowController.setRequestTopUi(requestTopUi, componentTag);
+ sysuiMainExecutor.execute(() -> {
+ mNotificationShadeWindowController.setRequestTopUi(requestTopUi, componentTag);
+ });
}
@Override
public void notifyRemoveNotification(String key, int reason) {
- final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- key);
- if (entry != null) {
- for (NotifCallback cb : mCallbacks) {
- cb.removeNotification(entry, getDismissedByUserStats(entry, true), reason);
+ sysuiMainExecutor.execute(() -> {
+ final NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ if (entry != null) {
+ for (NotifCallback cb : mCallbacks) {
+ cb.removeNotification(entry, getDismissedByUserStats(entry, true),
+ reason);
+ }
}
- }
+ });
}
@Override
public void notifyInvalidateNotifications(String reason) {
- for (NotifCallback cb : mCallbacks) {
- cb.invalidateNotifications(reason);
- }
+ sysuiMainExecutor.execute(() -> {
+ for (NotifCallback cb : mCallbacks) {
+ cb.invalidateNotifications(reason);
+ }
+ });
}
@Override
public void notifyMaybeCancelSummary(String key) {
- final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- key);
- if (entry != null) {
- for (NotifCallback cb : mCallbacks) {
- cb.maybeCancelSummary(entry);
+ sysuiMainExecutor.execute(() -> {
+ final NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ if (entry != null) {
+ for (NotifCallback cb : mCallbacks) {
+ cb.maybeCancelSummary(entry);
+ }
}
- }
+ });
}
@Override
public void removeNotificationEntry(String key) {
- final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- key);
- if (entry != null) {
- mNotificationGroupManager.onEntryRemoved(entry);
- }
+ sysuiMainExecutor.execute(() -> {
+ final NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ if (entry != null) {
+ mNotificationGroupManager.onEntryRemoved(entry);
+ }
+ });
}
@Override
public void updateNotificationBubbleButton(String key) {
- final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- key);
- if (entry != null && entry.getRow() != null) {
- entry.getRow().updateBubbleButton();
- }
+ sysuiMainExecutor.execute(() -> {
+ final NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ if (entry != null && entry.getRow() != null) {
+ entry.getRow().updateBubbleButton();
+ }
+ });
}
@Override
public void updateNotificationSuppression(String key) {
- final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- key);
- if (entry != null) {
- mNotificationGroupManager.updateSuppression(entry);
- }
+ sysuiMainExecutor.execute(() -> {
+ final NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ if (entry != null) {
+ mNotificationGroupManager.updateSuppression(entry);
+ }
+ });
}
@Override
public void onStackExpandChanged(boolean shouldExpand) {
- sysUiState
- .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
- .commitUpdate(mContext.getDisplayId());
+ sysuiMainExecutor.execute(() -> {
+ sysUiState.setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+ .commitUpdate(mContext.getDisplayId());
+ });
}
@Override
public void onUnbubbleConversation(String key) {
- final NotificationEntry entry =
- mNotificationEntryManager.getPendingOrActiveNotif(key);
- if (entry != null) {
- onUserChangedBubble(entry, false /* shouldBubble */);
- }
+ sysuiMainExecutor.execute(() -> {
+ final NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ if (entry != null) {
+ onUserChangedBubble(entry, false /* shouldBubble */);
+ }
+ });
}
};
mBubbles.setSysuiProxy(mSysuiProxy);
@@ -424,9 +484,8 @@
final String groupKey = group.summary != null
? group.summary.getSbn().getGroupKey()
: null;
- if (!suppressed && groupKey != null
- && mBubbles.isSummarySuppressed(groupKey)) {
- mBubbles.removeSuppressedSummary(groupKey);
+ if (!suppressed && groupKey != null) {
+ mBubbles.removeSuppressedSummaryIfNecessary(groupKey, null, null);
}
}
});
@@ -449,19 +508,16 @@
// Check if removed bubble has an associated suppressed group summary that needs
// to be removed now.
final String groupKey = entry.getSbn().getGroupKey();
- if (mBubbles.isSummarySuppressed(groupKey)) {
- mBubbles.removeSuppressedSummary(groupKey);
-
+ mBubbles.removeSuppressedSummaryIfNecessary(groupKey, (summaryKey) -> {
final NotificationEntry summary =
- mNotificationEntryManager.getActiveNotificationUnfiltered(
- mBubbles.getSummaryKey(groupKey));
+ mNotificationEntryManager.getActiveNotificationUnfiltered(summaryKey);
if (summary != null) {
mNotificationEntryManager.performRemoveNotification(
summary.getSbn(),
getDismissedByUserStats(summary, false),
UNDEFINED_DISMISS_REASON);
}
- }
+ }, mSysuiMainExecutor);
// Check if we still need to remove the summary from NoManGroup because the summary
// may not be in the mBubbleData.mSuppressedGroupKeys list and removed above.
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 103e6bb..b7aa907 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -71,6 +71,8 @@
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.sizecompatui.SizeCompatUI;
+import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
@@ -293,8 +295,8 @@
@WMSingleton
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
- Context context) {
- return new ShellTaskOrganizer(mainExecutor, context);
+ Context context, SizeCompatUI sizeCompatUI) {
+ return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI);
}
@WMSingleton
@@ -331,6 +333,7 @@
@BindsOptionalOf
abstract AppPairs optionalAppPairs();
+ // Note: Handler needed for LauncherApps.register
@WMSingleton
@Provides
static Optional<Bubbles> provideBubbles(Context context,
@@ -341,11 +344,12 @@
LauncherApps launcherApps,
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
- @ShellMainThread ShellExecutor mainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
return Optional.of(BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, uiEventLogger, organizer,
- mainExecutor));
+ mainExecutor, mainHandler));
}
// Needs the shell main handler for ContentObserver callbacks
@@ -378,8 +382,7 @@
@WMSingleton
@Provides
- static FullscreenTaskListener provideFullscreenTaskListener(
- SyncTransactionQueue syncQueue) {
+ static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) {
return new FullscreenTaskListener(syncQueue);
}
@@ -390,4 +393,12 @@
@ShellAnimationThread ShellExecutor animExecutor) {
return new Transitions(organizer, pool, mainExecutor, animExecutor);
}
+
+ @WMSingleton
+ @Provides
+ static SizeCompatUI provideSizeCompatUI(Context context, DisplayController displayController,
+ DisplayImeController imeController, @ShellMainThread ShellExecutor mainExecutor) {
+ return SizeCompatUIController.create(context, displayController, imeController,
+ mainExecutor);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
new file mode 100644
index 0000000..826be2b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.keyguard;
+
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.dagger.KeyguardStatusViewComponent;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.navigationbar.NavigationBarController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class KeyguardDisplayManagerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
+
+ @Mock
+ private DisplayManager mDisplayManager;
+
+ @Mock
+ private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
+
+ private Executor mBackgroundExecutor = Runnable::run;
+ private KeyguardDisplayManager mManager;
+
+ // The default and secondary displays are both in the default group
+ private Display mDefaultDisplay;
+ private Display mSecondaryDisplay;
+
+ // This display is in a different group from the default and secondary displays.
+ private Display mDifferentGroupDisplay;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
+ mDependency.injectMockDependency(NavigationBarController.class);
+ mManager = spy(new KeyguardDisplayManager(mContext, mKeyguardStatusViewComponentFactory,
+ mBackgroundExecutor));
+ doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());
+
+ mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
+ new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
+ mSecondaryDisplay = new Display(DisplayManagerGlobal.getInstance(),
+ Display.DEFAULT_DISPLAY + 1,
+ new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
+
+ DisplayInfo differentGroupInfo = new DisplayInfo();
+ differentGroupInfo.displayId = Display.DEFAULT_DISPLAY + 2;
+ differentGroupInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ mDifferentGroupDisplay = new Display(DisplayManagerGlobal.getInstance(),
+ Display.DEFAULT_DISPLAY,
+ differentGroupInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
+ }
+
+ @Test
+ public void testShow_defaultDisplayOnly() {
+ when(mDisplayManager.getDisplays()).thenReturn(new Display[]{mDefaultDisplay});
+ mManager.show();
+ verify(mManager, never()).createPresentation(any());
+ }
+
+ @Test
+ public void testShow_includeSecondaryDisplay() {
+ when(mDisplayManager.getDisplays()).thenReturn(
+ new Display[]{mDefaultDisplay, mSecondaryDisplay});
+ mManager.show();
+ verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
+ }
+
+ @Test
+ public void testShow_includeNonDefaultGroupDisplay() {
+ when(mDisplayManager.getDisplays()).thenReturn(
+ new Display[]{mDefaultDisplay, mDifferentGroupDisplay});
+
+ mManager.show();
+ verify(mManager, never()).createPresentation(any());
+ }
+
+ @Test
+ public void testShow_includeSecondaryAndNonDefaultGroupDisplays() {
+ when(mDisplayManager.getDisplays()).thenReturn(
+ new Display[]{mDefaultDisplay, mSecondaryDisplay, mDifferentGroupDisplay});
+
+ mManager.show();
+ verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
deleted file mode 100644
index 71a0434..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
-import android.testing.AndroidTestingRunner;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SizeCompatModeActivityController.RestartActivityButton;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.CommandQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * runtest systemui -c com.android.systemui.SizeCompatModeActivityControllerTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatModeActivityControllerTest extends SysuiTestCase {
- private static final int DISPLAY_ID = 0;
-
- private SizeCompatModeActivityController mController;
- private TaskStackChangeListener mTaskStackListener;
- private @Mock TaskStackChangeListeners mMockTaskListeners;
- private @Mock RestartActivityButton mMockButton;
- private @Mock IBinder mMockActivityToken;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- doReturn(true).when(mMockButton).show();
-
- mController = new SizeCompatModeActivityController(mContext, mMockTaskListeners,
- new CommandQueue(mContext)) {
- @Override
- RestartActivityButton createRestartButton(Context context) {
- return mMockButton;
- };
- };
-
- ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
- ArgumentCaptor.forClass(TaskStackChangeListener.class);
- verify(mMockTaskListeners).registerTaskStackListener(listenerCaptor.capture());
- mTaskStackListener = listenerCaptor.getValue();
- }
-
- @Test
- public void testOnSizeCompatModeActivityChanged() {
- // Verifies that the restart button is added with non-null component name.
- mTaskStackListener.onSizeCompatModeActivityChanged(DISPLAY_ID, mMockActivityToken);
- verify(mMockButton).show();
- verify(mMockButton).updateLastTargetActivity(eq(mMockActivityToken));
-
- // Verifies that the restart button is removed with null component name.
- mTaskStackListener.onSizeCompatModeActivityChanged(DISPLAY_ID, null /* activityToken */);
- verify(mMockButton).remove();
- }
-
- @Test
- public void testChangeButtonVisibilityOnImeShowHide() {
- mTaskStackListener.onSizeCompatModeActivityChanged(DISPLAY_ID, mMockActivityToken);
-
- // Verifies that the restart button is hidden when IME is visible.
- doReturn(View.VISIBLE).when(mMockButton).getVisibility();
- mController.setImeWindowStatus(DISPLAY_ID, null /* token */, InputMethodService.IME_VISIBLE,
- 0 /* backDisposition */, false /* showImeSwitcher */);
- verify(mMockButton).setVisibility(eq(View.GONE));
-
- // Verifies that the restart button is visible when IME is hidden.
- doReturn(View.GONE).when(mMockButton).getVisibility();
- mController.setImeWindowStatus(DISPLAY_ID, null /* token */, 0 /* vis */,
- 0 /* backDisposition */, false /* showImeSwitcher */);
- verify(mMockButton).setVisibility(eq(View.VISIBLE));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index dd145e4..3e873d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -27,7 +27,6 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.biometrics.SensorProperties;
-import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -45,9 +44,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
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.FakeExecutor;
-import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -86,15 +84,12 @@
@Mock
private FingerprintManager mFingerprintManager;
@Mock
- private DisplayManager mDisplayManager;
- @Mock
private WindowManager mWindowManager;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
- private ScrimController mScrimController;
+ private StatusBar mStatusBar;
- private FakeSettings mSystemSettings;
private FakeExecutor mFgExecutor;
// Stuff for configuring mocks
@@ -109,7 +104,7 @@
@Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
private IUdfpsOverlayController mOverlayController;
@Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
- @Captor private ArgumentCaptor<Runnable> mRunAfterShowingScrimAndDotCaptor;
+ @Captor private ArgumentCaptor<Runnable> mOnIlluminatedRunnableCaptor;
@Before
public void setUp() {
@@ -122,19 +117,16 @@
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */));
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
- mSystemSettings = new FakeSettings();
mFgExecutor = new FakeExecutor(new FakeSystemClock());
mUdfpsController = new UdfpsController(
mContext,
mResources,
mLayoutInflater,
mFingerprintManager,
- mDisplayManager,
mWindowManager,
- mSystemSettings,
mStatusBarStateController,
mFgExecutor,
- mScrimController);
+ mStatusBar);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
@@ -183,7 +175,7 @@
@Test
public void fingerDown() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isShowScrimAndDot()).thenReturn(false);
+ when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing
@@ -195,12 +187,10 @@
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
- // THEN the scrim and dot is shown
- verify(mUdfpsView).showScrimAndDot();
- // AND a runnable that passes the event to FingerprintManager is set on the view
- verify(mUdfpsView).setRunAfterShowingScrimAndDot(
- mRunAfterShowingScrimAndDotCaptor.capture());
- mRunAfterShowingScrimAndDotCaptor.getValue().run();
+ // THEN illumination begins
+ // AND onIlluminatedRunnable that notifies FingerprintManager is set
+ verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
+ mOnIlluminatedRunnableCaptor.getValue().run();
verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0),
eq(0), eq(0f), eq(0f));
}
@@ -213,12 +203,10 @@
mFgExecutor.runAllReady();
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
- // THEN the scrim and dot is shown
- verify(mUdfpsView).showScrimAndDot();
- // AND a runnable that passes the event to FingerprintManager is set on the view
- verify(mUdfpsView).setRunAfterShowingScrimAndDot(
- mRunAfterShowingScrimAndDotCaptor.capture());
- mRunAfterShowingScrimAndDotCaptor.getValue().run();
+ // THEN illumination begins
+ // AND onIlluminatedRunnable that notifies FingerprintManager is set
+ verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
+ mOnIlluminatedRunnableCaptor.getValue().run();
verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0),
eq(0), eq(3f) /* minor */, eq(2f) /* major */);
}
@@ -232,8 +220,8 @@
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
// WHEN it is cancelled
mUdfpsController.onCancelAodInterrupt();
- // THEN the scrim and dot is hidden
- verify(mUdfpsView).hideScrimAndDot();
+ // THEN the illumination is hidden
+ verify(mUdfpsView).stopIllumination();
}
@Test
@@ -246,13 +234,13 @@
// WHEN it times out
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- // THEN the scrim and dot is hidden
- verify(mUdfpsView).hideScrimAndDot();
+ // THEN the illumination is hidden
+ verify(mUdfpsView).stopIllumination();
}
@Test
public void registersViewForCallbacks() throws RemoteException {
verify(mStatusBarStateController).addCallback(mUdfpsView);
- verify(mScrimController).addScrimChangedListener(mUdfpsView);
+ verify(mStatusBar).addExpansionChangedListener(mUdfpsView);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
index 51cf501..47f4183 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.verify;
import android.view.View;
+import android.view.WindowInsetsController;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -87,6 +88,8 @@
@Test
public void testOnRotationProposalShowButtonShowNav() {
// No navigation bar should not call to set visibility state
+ mRotationButtonController.onBehaviorChanged(
+ WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
mRotationButtonController.onNavigationBarWindowVisibilityChange(false /* showing */);
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
false /* visible */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 2546813..f86b465 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -295,8 +295,9 @@
)
controller.showDialog(context)
exhaustExecutors()
- assertThat(dialogProvider.list).hasSize(1)
+ assertThat(dialogProvider.list).hasSize(2)
assertThat(dialogProvider.list?.get(0)?.lastActiveTimestamp).isEqualTo(1L)
+ assertThat(dialogProvider.list?.get(1)?.lastActiveTimestamp).isEqualTo(0L)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
index 9762fff..eb5dd4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
@@ -72,7 +72,7 @@
)
dialog = PrivacyDialog(context, list, starter)
dialog.show()
- dialog.requireViewById<View>(R.id.link).callOnClick()
+ dialog.requireViewById<View>(R.id.privacy_item).callOnClick()
verify(starter).invoke(PrivacyType.TYPE_MICROPHONE.permGroupName)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index 53ff957..fe2f5f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -146,7 +146,8 @@
mAssistantFeedbackController);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically demoted to Silent by the system. "
- + "Was this correct?", prompt.getText().toString());
+ + "Let the developer know your feedback. Was this correct?",
+ prompt.getText().toString());
}
@Test
@@ -157,7 +158,8 @@
mAssistantFeedbackController);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically ranked higher in your shade. "
- + "Was this correct?", prompt.getText().toString());
+ + "Let the developer know your feedback. Was this correct?",
+ prompt.getText().toString());
}
@Test
@@ -168,7 +170,7 @@
mAssistantFeedbackController);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically promoted to Default by the system. "
- + "Was this correct?",
+ + "Let the developer know your feedback. Was this correct?",
prompt.getText().toString());
}
@@ -180,7 +182,8 @@
mAssistantFeedbackController);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically ranked lower in your shade. "
- + "Was this correct?", prompt.getText().toString());
+ + "Let the developer know your feedback. Was this correct?",
+ prompt.getText().toString());
}
@Test
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 ceb4d84..461f64e 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
@@ -83,6 +83,7 @@
private NotificationStackScrollLayout mStackScroller; // Normally test this
private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below
+ private AmbientState mAmbientState;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private StatusBar mBar;
@@ -123,6 +124,9 @@
});
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+ // Interact with real instance of AmbientState.
+ mAmbientState = new AmbientState(mContext, mNotificationSectionsManager);
+
// The actual class under test. You may need to work with this class directly when
// testing anonymous class members of mStackScroller, like mMenuEventListener,
// which refer to members of NotificationStackScrollLayout. The spy
@@ -134,7 +138,8 @@
mNotificationSectionsManager,
mGroupMembershipManger,
mGroupExpansionManager,
- mStatusBarStateController
+ mStatusBarStateController,
+ mAmbientState
);
mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
mNotificationSwipeHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 23c0930..bdde822 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -88,7 +88,6 @@
@Mock private NotificationPanelViewController mNotificationPanel;
@Mock private View mAmbientIndicationContainer;
@Mock private BiometricUnlockController mBiometricUnlockController;
- @Mock private LockscreenLockIconController mLockscreenLockIconController;
@Mock private AuthController mAuthController;
@Before
@@ -100,7 +99,7 @@
mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
mKeyguardUpdateMonitor, mPulseExpansionHandler,
mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
- mLockscreenLockIconController, mAuthController, mNotificationIconAreaController);
+ mAuthController, mNotificationIconAreaController);
mDozeServiceHost.initialize(
mStatusBar,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 95a3505..60af16a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
@@ -18,7 +18,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,12 +29,12 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -45,6 +44,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -64,7 +64,7 @@
@Mock
private KeyguardIndicationController mKeyguardIndicationController;
@Mock
- private LockIcon mLockIcon; // TODO: make this not a mock once inject is removed.
+ private LockIcon mLockIcon;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
@@ -81,34 +81,36 @@
private Resources mResources;
@Mock
private HeadsUpManagerPhone mHeadsUpManagerPhone;
- @Mock
- private KeyguardSecurityModel mKeyguardSecurityModel;
private LockscreenLockIconController mLockIconController;
+
+ @Captor ArgumentCaptor<OnAttachStateChangeListener> mOnAttachStateChangeCaptor;
+ @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
+
private OnAttachStateChangeListener mOnAttachStateChangeListener;
+ private StatusBarStateController.StateListener mStatusBarStateListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mKeyguardUpdateMonitor.shouldShowLockIcon()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.canShowLockIcon()).thenReturn(true);
when(mLockIcon.getContext()).thenReturn(mContext);
- mLockIconController = new LockscreenLockIconController(
+ mLockIconController = new LockscreenLockIconController(mLockIcon,
mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
mShadeController, mAccessibilityController, mKeyguardIndicationController,
mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator,
mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources,
- mHeadsUpManagerPhone, mKeyguardSecurityModel);
+ mHeadsUpManagerPhone);
- ArgumentCaptor<OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
- ArgumentCaptor.forClass(OnAttachStateChangeListener.class);
+ when(mLockIcon.isAttachedToWindow()).thenReturn(true);
+ mLockIconController.init();
- doNothing().when(mLockIcon)
- .addOnAttachStateChangeListener(
- onAttachStateChangeListenerArgumentCaptor.capture());
- mLockIconController.attach(mLockIcon);
-
- mOnAttachStateChangeListener = onAttachStateChangeListenerArgumentCaptor.getValue();
+ verify(mLockIcon).addOnAttachStateChangeListener(
+ mOnAttachStateChangeCaptor.capture());
+ mOnAttachStateChangeListener = mOnAttachStateChangeCaptor.getValue();
+ verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture());
+ mStatusBarStateListener = mStateListenerCaptor.getValue();
}
@Test
@@ -133,23 +135,17 @@
@Test
public void testVisibility_Dozing() {
- ArgumentCaptor<StatusBarStateController.StateListener> sBStateListenerCaptor =
- ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
-
- mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
- verify(mStatusBarStateController).addCallback(sBStateListenerCaptor.capture());
-
when(mStatusBarStateController.isDozing()).thenReturn(true);
- sBStateListenerCaptor.getValue().onDozingChanged(true);
+ mStatusBarStateListener.onDozingChanged(true);
verify(mLockIcon).updateIconVisibility(false);
}
@Test
public void testVisibility_doNotShowLockIcon() {
- when(mKeyguardUpdateMonitor.shouldShowLockIcon()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.canShowLockIcon()).thenReturn(false);
+ mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
- mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
verify(mLockIcon).setVisibility(View.GONE);
}
}
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 aa7143e..8d4470b 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
@@ -80,6 +80,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -202,7 +203,8 @@
private ScrimController mScrimController;
@Mock
private MediaDataManager mMediaDataManager;
-
+ @Mock
+ private AmbientState mAmbientState;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -282,7 +284,8 @@
mAuthController,
new QSDetailDisplayer(),
mScrimController,
- mMediaDataManager);
+ mMediaDataManager,
+ mAmbientState);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 3b2e055..21368d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -54,6 +54,8 @@
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.utils.os.FakeHandler;
@@ -220,7 +222,8 @@
mScrimController = new ScrimController(mLightBarController,
mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
- mDockManager, mBlurUtils, mConfigurationController, mFeatureFlags);
+ mDockManager, mBlurUtils, mConfigurationController, mFeatureFlags,
+ new FakeExecutor(new FakeSystemClock()));
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble);
mScrimController.setAnimatorListener(mAnimatorListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 2c781ba..cae488a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -386,7 +386,6 @@
() -> mAssistManager,
configurationController,
mNotificationShadeWindowController,
- mLockscreenLockIconController,
mDozeParameters,
mScrimController,
mKeyguardLiftController,
@@ -436,6 +435,7 @@
// initialized automatically.
mStatusBar.mNotificationShadeWindowView = mNotificationShadeWindowView;
mStatusBar.mNotificationPanelViewController = mNotificationPanelViewController;
+ mStatusBar.mLockscreenLockIconController = mLockscreenLockIconController;
mStatusBar.mDozeScrimController = mDozeScrimController;
mStatusBar.mPresenter = mNotificationPresenter;
mStatusBar.mKeyguardIndicationController = mKeyguardIndicationController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index da1f5d3..f8b6383 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -37,6 +37,7 @@
import android.app.Instrumentation;
import android.content.Intent;
import android.net.ConnectivityManager;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
@@ -343,6 +344,8 @@
setConnectivityCommon(networkType, validated, isConnected);
if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
if (isConnected) {
+ mNetworkCallback.onAvailable(mock(Network.class),
+ new NetworkCapabilities(mNetCapabilities), new LinkProperties(), false);
mNetworkCallback.onCapabilitiesChanged(
mock(Network.class), new NetworkCapabilities(mNetCapabilities));
} else {
@@ -357,6 +360,8 @@
setConnectivityCommon(networkType, validated, isConnected);
if (networkType == NetworkCapabilities.TRANSPORT_CELLULAR) {
if (isConnected) {
+ mNetworkCallback.onAvailable(mock(Network.class),
+ new NetworkCapabilities(mNetCapabilities), new LinkProperties(), false);
mNetworkCallback.onCapabilitiesChanged(
mock(Network.class), new NetworkCapabilities(mNetCapabilities));
} else {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index ccc2eb3..76269dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -166,6 +166,7 @@
private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
private BubblesManager mBubblesManager;
+ // TODO(178618782): Move tests on the controller directly to the shell
private TestableBubbleController mBubbleController;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private NotificationEntryListener mEntryListener;
@@ -221,6 +222,9 @@
mTestableLooper = TestableLooper.get(this);
+ // For the purposes of this test, just run everything synchronously
+ ShellExecutor syncExecutor = new SyncExecutor();
+
mContext.addMockSystemService(FaceManager.class, mFaceManager);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
@@ -257,8 +261,9 @@
mSysUiStateBubblesExpanded =
(sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0);
+ // TODO: Fix
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
- mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner);
+ mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
@@ -273,7 +278,7 @@
);
when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
- when(mShellTaskOrganizer.getExecutor()).thenReturn(new FakeExecutor(new FakeSystemClock()));
+ when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
mBubbleController = new TestableBubbleController(
mContext,
mBubbleData,
@@ -286,12 +291,13 @@
mBubbleLogger,
mShellTaskOrganizer,
mPositioner,
- mock(ShellExecutor.class));
+ syncExecutor,
+ mock(Handler.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
mContext,
- mBubbleController,
+ mBubbleController.getImpl(),
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
@@ -306,7 +312,8 @@
mNotifPipeline,
mSysUiState,
mFeatureFlagsOldPipeline,
- mDumpManager);
+ mDumpManager,
+ syncExecutor);
// Get a reference to the BubbleController's entry listener
verify(mNotificationEntryManager, atLeastOnce())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 00f4e3a..5340ff7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -83,8 +83,6 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.BubbleData;
@@ -203,6 +201,9 @@
mTestableLooper = TestableLooper.get(this);
+ // For the purposes of this test, just run everything synchronously
+ ShellExecutor syncExecutor = new SyncExecutor();
+
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
@@ -227,7 +228,7 @@
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
- mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner);
+ mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
@@ -241,7 +242,7 @@
mock(Handler.class)
);
when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
- when(mShellTaskOrganizer.getExecutor()).thenReturn(new FakeExecutor(new FakeSystemClock()));
+ when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
mBubbleController = new TestableBubbleController(
mContext,
mBubbleData,
@@ -254,12 +255,13 @@
mBubbleLogger,
mShellTaskOrganizer,
mPositioner,
- mock(ShellExecutor.class));
+ syncExecutor,
+ mock(Handler.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
mContext,
- mBubbleController,
+ mBubbleController.getImpl(),
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
@@ -274,7 +276,8 @@
mNotifPipeline,
mSysUiState,
mFeatureFlagsNewPipeline,
- mDumpManager);
+ mDumpManager,
+ syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
// Get a reference to the BubbleController's entry listener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java
new file mode 100644
index 0000000..d40eecf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java
@@ -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.systemui.wmshell;
+
+import com.android.wm.shell.common.ShellExecutor;
+
+/**
+ * And executor that just executes everything synchronously. To be removed once we move the
+ * tests of shell behavior over to the shell.
+ */
+public class SyncExecutor implements ShellExecutor {
+ @Override
+ public void execute(Runnable runnable) {
+ runnable.run();
+ }
+
+ @Override
+ public void executeDelayed(Runnable runnable, long delayMillis) {
+ runnable.run();
+ }
+
+ @Override
+ public void removeAllCallbacks() {
+ }
+
+ @Override
+ public void removeCallbacks(Runnable runnable) {
+ }
+
+ @Override
+ public boolean hasCallback(Runnable runnable) {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 3f918e8..cdf47b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -49,10 +49,11 @@
BubbleLogger bubbleLogger,
ShellTaskOrganizer shellTaskOrganizer,
BubblePositioner positioner,
- ShellExecutor shellMainExecutor) {
+ ShellExecutor shellMainExecutor,
+ Handler shellMainHandler) {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- bubbleLogger, shellTaskOrganizer, positioner, shellMainExecutor);
+ bubbleLogger, shellTaskOrganizer, positioner, shellMainExecutor, shellMainHandler);
setInflateSynchronously(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 4078d4d..79641db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -18,7 +18,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.test.suitebuilder.annotation.SmallTest;
@@ -41,7 +40,6 @@
import com.android.wm.shell.onehanded.OneHandedGestureHandler;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.pip.phone.PipTouchHandler;
import org.junit.Before;
import org.junit.Test;
@@ -51,6 +49,12 @@
import java.util.Optional;
+/**
+ * Tests for {@link WMShell}.
+ *
+ * Build/Install/Run:
+ * atest SystemUITests:WMShellTest
+ */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WMShellTest extends SysuiTestCase {
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index ae024ff..5dd271c 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -260,6 +260,10 @@
// Package: android
NOTE_ADB_WIFI_ACTIVE = 62;
+ // Notify the user a carrier suggestion is available to get IMSI exemption.
+ // Package: android
+ NOTE_CARRIER_SUGGESTION_AVAILABLE = 63;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/Android.bp b/services/Android.bp
index f6bb72a..61591c2 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -28,6 +28,7 @@
":services.profcollect-sources",
":services.restrictions-sources",
":services.searchui-sources",
+ ":services.smartspace-sources",
":services.speech-sources",
":services.startop.iorap-sources",
":services.systemcaptions-sources",
@@ -36,6 +37,7 @@
":services.usb-sources",
":services.voiceinteraction-sources",
":services.wifi-sources",
+ ":service-media-s-sources", // TODO (b/177640454)
":service-permission-sources",
":service-statsd-sources",
],
@@ -76,6 +78,7 @@
"services.profcollect",
"services.restrictions",
"services.searchui",
+ "services.smartspace",
"services.speech",
"services.startop",
"services.systemcaptions",
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9aa0aed..ea1473e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -166,8 +166,6 @@
// their capabilities are ready.
private static final int WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS = 1000;
- static final String FUNCTION_REGISTER_SYSTEM_ACTION = "registerSystemAction";
- static final String FUNCTION_UNREGISTER_SYSTEM_ACTION = "unregisterSystemAction";
private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE =
"registerUiTestAutomationService";
@@ -748,9 +746,7 @@
*/
@Override
public void registerSystemAction(RemoteAction action, int actionId) {
- mSecurityPolicy.enforceCallerIsRecentsOrHasPermission(
- Manifest.permission.MANAGE_ACCESSIBILITY,
- FUNCTION_REGISTER_SYSTEM_ACTION);
+ mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().registerSystemAction(actionId, action);
}
@@ -761,9 +757,7 @@
*/
@Override
public void unregisterSystemAction(int actionId) {
- mSecurityPolicy.enforceCallerIsRecentsOrHasPermission(
- Manifest.permission.MANAGE_ACCESSIBILITY,
- FUNCTION_UNREGISTER_SYSTEM_ACTION);
+ mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().unregisterSystemAction(actionId);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index d766431..bef6d3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -38,8 +38,6 @@
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
-import com.android.server.wm.ActivityTaskManagerInternal;
import libcore.util.EmptyArray;
@@ -88,7 +86,6 @@
private final AccessibilityUserManager mAccessibilityUserManager;
private AccessibilityWindowManager mAccessibilityWindowManager;
- private final ActivityTaskManagerInternal mAtmInternal;
/**
* Constructor for AccessibilityManagerService.
@@ -100,7 +97,6 @@
mPackageManager = mContext.getPackageManager();
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
}
/**
@@ -572,13 +568,4 @@
+ permission);
}
}
-
- /**
- * Enforcing permission check to IPC caller or grant it if it's recents.
- *
- * @param permission The permission to check
- */
- public void enforceCallerIsRecentsOrHasPermission(@NonNull String permission, String func) {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(permission, func);
- }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
index 7824fd9..9c54100 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
@@ -44,7 +44,7 @@
@Override
protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mCompletedTapCount + 1 == mTargetFingerCount) {
+ if (mCompletedTapCount + 1 == mTargetTapCount) {
// Calling super.onUp would complete the multi-tap version of this.
cancelGesture(event, rawEvent, policyFlags);
} else {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 04e08ae..55e3ef2 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -926,7 +926,7 @@
mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
deviceProfile, FgThread.getExecutor(), desc -> {
try {
- result.complete(requireNonNull(desc));
+ result.complete(desc);
} catch (Exception e) {
result.completeExceptionally(e);
}
diff --git a/services/core/java/android/power/PowerStatsInternal.java b/services/core/java/android/power/PowerStatsInternal.java
index f7417da..40107a5 100644
--- a/services/core/java/android/power/PowerStatsInternal.java
+++ b/services/core/java/android/power/PowerStatsInternal.java
@@ -18,8 +18,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
import java.util.concurrent.CompletableFuture;
@@ -51,4 +55,45 @@
@NonNull
public abstract CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
int[] energyConsumerIds);
+
+ /**
+ * Returns the power entity info for all available {@link PowerEntity}
+ *
+ * @return List of available {@link PowerEntity}
+ */
+ public abstract PowerEntity[] getPowerEntityInfo();
+
+ /**
+ * Returns a CompletableFuture that will get a {@link StateResidencyResult} array for the
+ * available requested power entities.
+ *
+ * @param powerEntityIds Array of {@link PowerEntity.id} for which state residency is being
+ * requested.
+ *
+ * @return A Future containing a list of {@link StateResidencyResult} objects containing state
+ * residency results for all listed {@link PowerEntity.id}.
+ */
+ @NonNull
+ public abstract CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+ int[] powerEntityIds);
+
+ /**
+ * Returns the channel info for all available {@link Channel}
+ *
+ * @return List of available {@link Channel}
+ */
+ public abstract Channel[] getEnergyMeterInfo();
+
+ /**
+ * Returns a CompletableFuture that will get a {@link EnergyMeasurement} array for the
+ * available requested channels.
+ *
+ * @param channelIds Array of {@link Channel.id} for accumulated energy is being requested.
+ *
+ * @return A Future containing a list of {@link EnergyMeasurement} objects containing
+ * accumulated energy measurements for all listed {@link Channel.id}.
+ */
+ @NonNull
+ public abstract CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+ int[] channelIds);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d129b9c..08390b4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -132,8 +132,8 @@
import android.net.TetheringManager;
import android.net.UidRange;
import android.net.UidRangeParcel;
+import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
-import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.metrics.INetdEventListener;
@@ -216,14 +216,13 @@
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.utils.PriorityDump;
-import com.google.android.collect.Lists;
-
import libcore.io.IoUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -992,6 +991,15 @@
}
/**
+ * Gets the UID that owns a socket connection. Needed because opening SOCK_DIAG sockets
+ * requires CAP_NET_ADMIN, which the unit tests do not have.
+ */
+ public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote) {
+ return InetDiagMessage.getConnectionOwnerUid(protocol, local, remote);
+ }
+
+ /**
* @see MultinetworkPolicyTracker
*/
public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(
@@ -1023,11 +1031,13 @@
mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
mMetricsLog = logger;
- mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
mNetworkRanker = new NetworkRanker();
- NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
- mNetworkRequests.put(mDefaultRequest, defaultNRI);
- mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
+ final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport(
+ -1, NetworkRequest.Type.REQUEST);
+ mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder());
+ mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
+ mDefaultNetworkRequests.add(mDefaultRequest);
+ mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
@@ -1329,31 +1339,6 @@
return mNextNetworkRequestId++;
}
- private NetworkState getFilteredNetworkState(int networkType, int uid) {
- if (mLegacyTypeTracker.isTypeSupported(networkType)) {
- final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- final NetworkState state;
- if (nai != null) {
- state = nai.getNetworkState();
- state.networkInfo.setType(networkType);
- } else {
- final NetworkInfo info = new NetworkInfo(networkType, 0,
- getNetworkTypeName(networkType), "");
- info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
- info.setIsAvailable(true);
- final NetworkCapabilities capabilities = new NetworkCapabilities();
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING,
- !info.isRoaming());
- state = new NetworkState(info, new LinkProperties(), capabilities,
- null, null, null);
- }
- filterNetworkStateForUid(state, uid, false);
- return state;
- } else {
- return NetworkState.EMPTY;
- }
- }
-
@VisibleForTesting
protected NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
if (network == null) {
@@ -1464,6 +1449,18 @@
"%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
}
+ private void filterNetworkInfo(@NonNull NetworkInfo networkInfo,
+ @NonNull NetworkCapabilities nc, int uid, boolean ignoreBlocked) {
+ if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
+ networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
+ }
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ mLockdownTracker.augmentNetworkInfo(networkInfo);
+ }
+ }
+ }
+
/**
* Apply any relevant filters to {@link NetworkState} for the given UID. For
* example, this may mark the network as {@link DetailedState#BLOCKED} based
@@ -1471,16 +1468,7 @@
*/
private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) {
if (state == null || state.networkInfo == null || state.linkProperties == null) return;
-
- if (isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid,
- ignoreBlocked)) {
- state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
- }
- synchronized (mVpns) {
- if (mLockdownTracker != null) {
- mLockdownTracker.augmentNetworkInfo(state.networkInfo);
- }
- }
+ filterNetworkInfo(state.networkInfo, state.networkCapabilities, uid, ignoreBlocked);
}
/**
@@ -1545,6 +1533,27 @@
return state.networkInfo;
}
+ private NetworkInfo getFilteredNetworkInfo(int networkType, int uid) {
+ if (!mLegacyTypeTracker.isTypeSupported(networkType)) {
+ return null;
+ }
+ final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ final NetworkInfo info;
+ final NetworkCapabilities nc;
+ if (nai != null) {
+ info = new NetworkInfo(nai.networkInfo);
+ info.setType(networkType);
+ nc = nai.networkCapabilities;
+ } else {
+ info = new NetworkInfo(networkType, 0, getNetworkTypeName(networkType), "");
+ info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+ info.setIsAvailable(true);
+ nc = new NetworkCapabilities();
+ }
+ filterNetworkInfo(info, nc, uid, false);
+ return info;
+ }
+
@Override
public NetworkInfo getNetworkInfo(int networkType) {
enforceAccessPermission();
@@ -1559,8 +1568,7 @@
return state.networkInfo;
}
}
- final NetworkState state = getFilteredNetworkState(networkType, uid);
- return state.networkInfo;
+ return getFilteredNetworkInfo(networkType, uid);
}
@Override
@@ -1579,7 +1587,7 @@
@Override
public NetworkInfo[] getAllNetworkInfo() {
enforceAccessPermission();
- final ArrayList<NetworkInfo> result = Lists.newArrayList();
+ final ArrayList<NetworkInfo> result = new ArrayList<>();
for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE;
networkType++) {
NetworkInfo info = getNetworkInfo(networkType);
@@ -1593,10 +1601,16 @@
@Override
public Network getNetworkForType(int networkType) {
enforceAccessPermission();
+ if (!mLegacyTypeTracker.isTypeSupported(networkType)) {
+ return null;
+ }
+ final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai == null) {
+ return null;
+ }
final int uid = mDeps.getCallingUid();
- NetworkState state = getFilteredNetworkState(networkType, uid);
- if (!isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid, false)) {
- return state.network;
+ if (!isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid, false)) {
+ return nai.network;
}
return null;
}
@@ -1634,7 +1648,7 @@
HashMap<Network, NetworkCapabilities> result = new HashMap<>();
- NetworkAgentInfo nai = getDefaultNetwork();
+ final NetworkAgentInfo nai = getDefaultNetwork();
NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
result.put(
@@ -1847,7 +1861,7 @@
// This contains IMSI details, so make sure the caller is privileged.
NetworkStack.checkNetworkStackPermission(mContext);
- final ArrayList<NetworkState> result = Lists.newArrayList();
+ final ArrayList<NetworkState> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai != null) {
@@ -2021,7 +2035,7 @@
// TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one
// callback from each caller type. Need to re-factor NetdEventListenerService to allow
// multiple NetworkMonitor registrants.
- if (nai != null && nai.satisfies(mDefaultRequest)) {
+ if (nai != null && nai.satisfies(mDefaultRequest.mRequests.get(0))) {
nai.networkMonitor().notifyDnsResponse(returnCode);
}
}
@@ -3439,18 +3453,23 @@
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
nri.setSatisfier(null, null);
sendUpdatedScoreToFactories(request, null);
+
+ if (mDefaultRequest == nri) {
+ // TODO : make battery stats aware that since 2013 multiple interfaces may be
+ // active at the same time. For now keep calling this with the default
+ // network, because while incorrect this is the closest to the old (also
+ // incorrect) behavior.
+ mNetworkActivityTracker.updateDataActivityTracking(
+ null /* newNetwork */, nai);
+ notifyLockdownVpn(nai);
+ ensureNetworkTransitionWakelock(nai.toShortString());
+ }
}
}
nai.clearLingerState();
- // TODO: this loop, and the mLegacyTypeTracker.remove just below it, seem redundant given
- // there's a full rematch right after. Currently, deleting it breaks tests that check for
- // the default network disconnecting. Find out why, fix the rematch code, and delete this.
- if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
- mDefaultNetworkNai = null;
- mNetworkActivityTracker.updateDataActivityTracking(null /* newNetwork */, nai);
- notifyLockdownVpn(nai);
- ensureNetworkTransitionWakelock(nai.toShortString());
- }
+ // TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
+ // Currently, deleting it breaks tests that check for the default network disconnecting.
+ // Find out why, fix the rematch code, and delete this.
mLegacyTypeTracker.remove(nai, wasDefault);
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
@@ -3459,10 +3478,9 @@
// (routing rules, DNS, etc).
// This may be slow as it requires a lot of netd shelling out to ip and
// ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
- // after we've rematched networks with requests which should make a potential
- // fallback network the default or requested a new network from the
- // NetworkProviders, so network traffic isn't interrupted for an unnecessarily
- // long time.
+ // after we've rematched networks with requests (which might change the default
+ // network or service a new request from an app), so network traffic isn't interrupted
+ // for an unnecessarily long time.
destroyNativeNetwork(nai);
mDnsManager.removeNetwork(nai.network);
}
@@ -4251,7 +4269,7 @@
@Override
public NetworkRequest getDefaultRequest() {
- return mDefaultRequest;
+ return mDefaultRequest.mRequests.get(0);
}
private class InternalHandler extends Handler {
@@ -4829,7 +4847,7 @@
}
synchronized (mVpns) {
throwIfLockdownEnabled();
- mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
+ mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
}
}
@@ -4854,35 +4872,35 @@
*
* <p>Must be called on the handler thread.
*/
- private VpnInfo[] getAllVpnInfo() {
+ private UnderlyingNetworkInfo[] getAllVpnInfo() {
ensureRunningOnConnectivityServiceThread();
synchronized (mVpns) {
if (mLockdownEnabled) {
- return new VpnInfo[0];
+ return new UnderlyingNetworkInfo[0];
}
}
- List<VpnInfo> infoList = new ArrayList<>();
+ List<UnderlyingNetworkInfo> infoList = new ArrayList<>();
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- VpnInfo info = createVpnInfo(nai);
+ UnderlyingNetworkInfo info = createVpnInfo(nai);
if (info != null) {
infoList.add(info);
}
}
- return infoList.toArray(new VpnInfo[infoList.size()]);
+ return infoList.toArray(new UnderlyingNetworkInfo[infoList.size()]);
}
/**
* @return VPN information for accounting, or null if we can't retrieve all required
* information, e.g underlying ifaces.
*/
- private VpnInfo createVpnInfo(NetworkAgentInfo nai) {
+ private UnderlyingNetworkInfo createVpnInfo(NetworkAgentInfo nai) {
if (!nai.isVPN()) return null;
Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
if (underlyingNetworks == null) {
- NetworkAgentInfo defaultNai = getDefaultNetwork();
+ final NetworkAgentInfo defaultNai = getDefaultNetwork();
if (defaultNai != null) {
underlyingNetworks = new Network[] { defaultNai.network };
}
@@ -4907,11 +4925,11 @@
// Must be non-null or NetworkStatsService will crash.
// Cannot happen in production code because Vpn only registers the NetworkAgent after the
// tun or ipsec interface is created.
+ // TODO: Remove this check.
if (nai.linkProperties.getInterfaceName() == null) return null;
- return new VpnInfo(nai.networkCapabilities.getOwnerUid(),
- nai.linkProperties.getInterfaceName(),
- interfaces.toArray(new String[0]));
+ return new UnderlyingNetworkInfo(nai.networkCapabilities.getOwnerUid(),
+ nai.linkProperties.getInterfaceName(), interfaces);
}
/**
@@ -5491,6 +5509,8 @@
*/
@VisibleForTesting
protected class NetworkRequestInfo implements IBinder.DeathRecipient {
+ // The requests to be satisfied in priority order. Non-multilayer requests will only have a
+ // single NetworkRequest in mRequests.
final List<NetworkRequest> mRequests;
// mSatisfier and mActiveRequest rely on one another therefore set them together.
@@ -5707,6 +5727,9 @@
networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
enforceAccessPermission();
break;
+ case BACKGROUND_REQUEST:
+ enforceNetworkStackOrSettingsPermission();
+ // Fall-through since other checks are the same with normal requests.
case REQUEST:
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
@@ -6033,11 +6056,13 @@
@GuardedBy("mBlockedAppUids")
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
+ // The always-on request for an Internet-capable network that apps without a specific default
+ // fall back to.
@NonNull
- private final NetworkRequest mDefaultRequest;
- // The NetworkAgentInfo currently satisfying the default request, if any.
- @Nullable
- private volatile NetworkAgentInfo mDefaultNetworkNai = null;
+ private final NetworkRequestInfo mDefaultRequest;
+ // Collection of NetworkRequestInfo's used for default networks.
+ @NonNull
+ private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
// Request used to optionally keep mobile data active even when higher
// priority networks like Wi-Fi are active.
@@ -6050,8 +6075,10 @@
// Request used to optionally keep vehicle internal network always active
private final NetworkRequest mDefaultVehicleRequest;
+ // TODO: b/178729499 update this in favor of a method taking in a UID.
+ // The NetworkAgentInfo currently satisfying the default request, if any.
private NetworkAgentInfo getDefaultNetwork() {
- return mDefaultNetworkNai;
+ return mDefaultRequest.mSatisfier;
}
@Nullable
@@ -7179,14 +7206,41 @@
}
}
- private void makeDefault(@Nullable final NetworkAgentInfo newNetwork) {
- if (DBG) log("Switching to new default network: " + newNetwork);
+ private void processDefaultNetworkChanges(@NonNull final NetworkReassignment changes) {
+ boolean isDefaultChanged = false;
+ for (final NetworkRequestInfo defaultRequestInfo : mDefaultNetworkRequests) {
+ final NetworkReassignment.RequestReassignment reassignment =
+ changes.getReassignment(defaultRequestInfo);
+ if (null == reassignment) {
+ continue;
+ }
+ // reassignment only contains those instances where the satisfying network changed.
+ isDefaultChanged = true;
+ // Notify system services of the new default.
+ makeDefault(defaultRequestInfo, reassignment.mOldNetwork, reassignment.mNewNetwork);
+ }
- mDefaultNetworkNai = newNetwork;
+ if (isDefaultChanged) {
+ // Hold a wakelock for a short time to help apps in migrating to a new default.
+ scheduleReleaseNetworkTransitionWakelock();
+ }
+ }
+
+ private void makeDefault(@NonNull final NetworkRequestInfo nri,
+ @Nullable final NetworkAgentInfo oldDefaultNetwork,
+ @Nullable final NetworkAgentInfo newDefaultNetwork) {
+ if (DBG) {
+ log("Switching to new default network for: " + nri + " using " + newDefaultNetwork);
+ }
try {
- if (null != newNetwork) {
- mNetd.networkSetDefault(newNetwork.network.getNetId());
+ // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes.
+ if (mDefaultRequest != nri) {
+ return;
+ }
+
+ if (null != newDefaultNetwork) {
+ mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
} else {
mNetd.networkClearDefault();
}
@@ -7194,16 +7248,41 @@
loge("Exception setting default network :" + e);
}
- notifyLockdownVpn(newNetwork);
- handleApplyDefaultProxy(null != newNetwork
- ? newNetwork.linkProperties.getHttpProxy() : null);
- updateTcpBufferSizes(null != newNetwork
- ? newNetwork.linkProperties.getTcpBufferSizes() : null);
+ if (oldDefaultNetwork != null) {
+ mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
+ }
+ mNetworkActivityTracker.updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
+ notifyLockdownVpn(newDefaultNetwork);
+ handleApplyDefaultProxy(null != newDefaultNetwork
+ ? newDefaultNetwork.linkProperties.getHttpProxy() : null);
+ updateTcpBufferSizes(null != newDefaultNetwork
+ ? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
notifyIfacesChangedForNetworkStats();
// Fix up the NetworkCapabilities of any networks that have this network as underlying.
- if (newNetwork != null) {
- propagateUnderlyingNetworkCapabilities(newNetwork.network);
+ if (newDefaultNetwork != null) {
+ propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
}
+
+ // Log 0 -> X and Y -> X default network transitions, where X is the new default.
+ final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
+ final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
+ final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
+ final LinkProperties lp = (newDefaultNetwork != null)
+ ? newDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities nc = (newDefaultNetwork != null)
+ ? newDefaultNetwork.networkCapabilities : null;
+
+ final Network prevNetwork = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.network : null;
+ final int prevScore = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.getCurrentScore() : 0;
+ final LinkProperties prevLp = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.networkCapabilities : null;
+
+ mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
+ prevNetwork, prevScore, prevLp, prevNc);
}
private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -7455,46 +7534,8 @@
now);
}
- final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
- final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
- final NetworkReassignment.RequestReassignment reassignment =
- changes.getReassignment(defaultRequestInfo);
- final NetworkAgentInfo newDefaultNetwork =
- null != reassignment ? reassignment.mNewNetwork : oldDefaultNetwork;
-
- if (oldDefaultNetwork != newDefaultNetwork) {
- if (oldDefaultNetwork != null) {
- mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
- }
- mNetworkActivityTracker.updateDataActivityTracking(
- newDefaultNetwork, oldDefaultNetwork);
- // Notify system services of the new default.
- makeDefault(newDefaultNetwork);
-
- // Log 0 -> X and Y -> X default network transitions, where X is the new default.
- final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
- final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
- final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
- final LinkProperties lp = (newDefaultNetwork != null)
- ? newDefaultNetwork.linkProperties : null;
- final NetworkCapabilities nc = (newDefaultNetwork != null)
- ? newDefaultNetwork.networkCapabilities : null;
-
- final Network prevNetwork = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.network : null;
- final int prevScore = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.getCurrentScore() : 0;
- final LinkProperties prevLp = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.linkProperties : null;
- final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.networkCapabilities : null;
-
- mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
- prevNetwork, prevScore, prevLp, prevNc);
-
- // Have a new default network, release the transition wakelock in
- scheduleReleaseNetworkTransitionWakelock();
- }
+ // Process default network changes if applicable.
+ processDefaultNetworkChanges(changes);
// Notify requested networks are available after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
@@ -7547,7 +7588,7 @@
notifyNetworkLosing(nai, now);
}
- updateLegacyTypeTrackerAndVpnLockdownForRematch(oldDefaultNetwork, newDefaultNetwork, nais);
+ updateLegacyTypeTrackerAndVpnLockdownForRematch(changes, nais);
// Tear down all unneeded networks.
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
@@ -7590,9 +7631,15 @@
}
private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
- @Nullable final NetworkAgentInfo oldDefaultNetwork,
- @Nullable final NetworkAgentInfo newDefaultNetwork,
+ @NonNull final NetworkReassignment changes,
@NonNull final Collection<NetworkAgentInfo> nais) {
+ final NetworkReassignment.RequestReassignment reassignmentOfDefault =
+ changes.getReassignment(mDefaultRequest);
+ final NetworkAgentInfo oldDefaultNetwork =
+ null != reassignmentOfDefault ? reassignmentOfDefault.mOldNetwork : null;
+ final NetworkAgentInfo newDefaultNetwork =
+ null != reassignmentOfDefault ? reassignmentOfDefault.mNewNetwork : null;
+
if (oldDefaultNetwork != newDefaultNetwork) {
// Maintain the illusion : since the legacy API only understands one network at a time,
// if the default network changed, apps should see a disconnected broadcast for the
@@ -7606,7 +7653,8 @@
// network doesn't satisfy the default request any more because it lost a
// capability.
mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
- mLegacyTypeTracker.add(newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
+ mLegacyTypeTracker.add(
+ newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
// If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
// to reflect the NetworkInfo of this new network. This broadcast has to be sent
// after the disconnect broadcasts above, but before the broadcasts sent by the
@@ -7658,7 +7706,7 @@
private void updateInetCondition(NetworkAgentInfo nai) {
// Don't bother updating until we've graduated to validated at least once.
if (!nai.everValidated) return;
- // For now only update icons for default connection.
+ // For now only update icons for the default connection.
// TODO: Update WiFi and cellular icons separately. b/17237507
if (!isDefaultNetwork(nai)) return;
@@ -7928,7 +7976,7 @@
intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
}
NetworkAgentInfo newDefaultAgent = null;
- if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
+ if (nai.isSatisfyingRequest(mDefaultRequest.mRequests.get(0).requestId)) {
newDefaultAgent = getDefaultNetwork();
if (newDefaultAgent != null) {
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
@@ -7976,8 +8024,8 @@
*/
private Network[] getDefaultNetworks() {
ensureRunningOnConnectivityServiceThread();
- ArrayList<Network> defaultNetworks = new ArrayList<>();
- NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+ final ArrayList<Network> defaultNetworks = new ArrayList<>();
+ final NetworkAgentInfo defaultNetwork = getDefaultNetwork();
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) {
defaultNetworks.add(nai.network);
@@ -7998,10 +8046,10 @@
activeIface = activeLinkProperties.getInterfaceName();
}
- final VpnInfo[] vpnInfos = getAllVpnInfo();
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos = getAllVpnInfo();
try {
- mStatsService.forceUpdateIfaces(
- getDefaultNetworks(), getAllNetworkState(), activeIface, vpnInfos);
+ mStatsService.forceUpdateIfaces(getDefaultNetworks(), getAllNetworkState(), activeIface,
+ underlyingNetworkInfos);
} catch (Exception ignored) {
}
}
@@ -8029,7 +8077,6 @@
int user = UserHandle.getUserId(mDeps.getCallingUid());
final boolean success;
synchronized (mVpns) {
- throwIfLockdownEnabled();
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
return success;
@@ -8261,6 +8308,7 @@
return getVpnIfOwner(mDeps.getCallingUid());
}
+ // TODO: stop calling into Vpn.java and get this information from data in this class.
@GuardedBy("mVpns")
private Vpn getVpnIfOwner(int uid) {
final int user = UserHandle.getUserId(uid);
@@ -8269,7 +8317,7 @@
if (vpn == null) {
return null;
} else {
- final VpnInfo info = vpn.getVpnInfo();
+ final UnderlyingNetworkInfo info = vpn.getUnderlyingNetworkInfo();
return (info == null || info.ownerUid != uid) ? null : vpn;
}
}
@@ -8311,7 +8359,7 @@
throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
}
- final int uid = InetDiagMessage.getConnectionOwnerUid(connectionInfo.protocol,
+ final int uid = mDeps.getConnectionOwnerUid(connectionInfo.protocol,
connectionInfo.local, connectionInfo.remote);
/* Filter out Uids not associated with the VPN. */
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 8b506ba..41903fc 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -372,10 +372,12 @@
* even from a previous boot.
*/
public void unregisterHealthObserver(PackageHealthObserver observer) {
- synchronized (mLock) {
- mAllObservers.remove(observer.getName());
- }
- syncState("unregistering observer: " + observer.getName());
+ mLongTaskHandler.post(() -> {
+ synchronized (mLock) {
+ mAllObservers.remove(observer.getName());
+ }
+ syncState("unregistering observer: " + observer.getName());
+ });
}
/**
@@ -982,7 +984,11 @@
if (!DeviceConfig.NAMESPACE_ROLLBACK.equals(properties.getNamespace())) {
return;
}
- updateConfigs();
+ try {
+ updateConfigs();
+ } catch (Exception ignore) {
+ Slog.w(TAG, "Failed to reload device config changes");
+ }
});
}
@@ -990,7 +996,8 @@
* Health check is enabled or disabled after reading the flags
* from DeviceConfig.
*/
- private void updateConfigs() {
+ @VisibleForTesting
+ void updateConfigs() {
synchronized (mLock) {
mTriggerFailureCount = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ROLLBACK,
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0412f08..7d65156 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -48,6 +48,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
@@ -482,14 +483,21 @@
}
}
- private @Nullable VolumeInfo findStorageForUuid(String volumeUuid) {
+ private @Nullable VolumeInfo findStorageForUuidAsUser(String volumeUuid,
+ @UserIdInt int userId) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
- return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL + ";" + 0);
+ return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL + ";" + userId);
} else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
return storage.getPrimaryPhysicalVolume();
} else {
- return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid));
+ VolumeInfo info = storage.findVolumeByUuid(volumeUuid);
+ if (info == null) {
+ Slog.w(TAG, "findStorageForUuidAsUser cannot find volumeUuid:" + volumeUuid);
+ return null;
+ }
+ String emulatedUuid = info.getId().replace("private", "emulated") + ";" + userId;
+ return storage.findVolumeById(emulatedUuid);
}
}
@@ -2605,8 +2613,9 @@
return;
} else {
- from = findStorageForUuid(mPrimaryStorageUuid);
- to = findStorageForUuid(volumeUuid);
+ int currentUserId = mCurrentUserId;
+ from = findStorageForUuidAsUser(mPrimaryStorageUuid, currentUserId);
+ to = findStorageForUuidAsUser(volumeUuid, currentUserId);
if (from == null) {
Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid);
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index a08d066..e96fd39 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -32,6 +32,7 @@
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
+import android.net.NetworkStack;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.TestNetworkInterface;
@@ -48,6 +49,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.NetdUtils;
import java.io.UncheckedIOException;
import java.net.Inet4Address;
@@ -317,10 +319,10 @@
}
try {
- // This requires NETWORK_STACK privileges.
final long token = Binder.clearCallingIdentity();
try {
- mNMS.setInterfaceUp(iface);
+ NetworkStack.checkNetworkStackPermission(mContext);
+ NetdUtils.setInterfaceUp(mNetd, iface);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 76db019..6a72010 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -25,9 +25,14 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnUnderlyingNetworkPolicy;
+import android.net.wifi.WifiInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -288,6 +293,12 @@
@NonNull VcnConfig config) {
return new Vcn(vcnContext, subscriptionGroup, config);
}
+
+ /** Gets the subId indicated by the given {@link WifiInfo}. */
+ public int getSubIdForWifiInfo(@NonNull WifiInfo wifiInfo) {
+ // TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
}
/** Notifies the VcnManagementService that external dependencies can be set up. */
@@ -527,7 +538,7 @@
@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
requireNonNull(listener, "listener was null");
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.NETWORK_FACTORY,
"Must have permission NETWORK_FACTORY to register a policy listener");
@@ -561,4 +572,55 @@
}
}
}
+
+ /**
+ * Gets the UnderlyingNetworkPolicy as determined by the provided NetworkCapabilities and
+ * LinkProperties.
+ */
+ @NonNull
+ @Override
+ public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties) {
+ requireNonNull(networkCapabilities, "networkCapabilities was null");
+ requireNonNull(linkProperties, "linkProperties was null");
+
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_FACTORY,
+ "Must have permission NETWORK_FACTORY or be the SystemServer to get underlying"
+ + " Network policies");
+
+ // Defensive copy in case this call is in-process and the given NetworkCapabilities mutates
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && networkCapabilities.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
+ TelephonyNetworkSpecifier telephonyNetworkSpecifier =
+ (TelephonyNetworkSpecifier) networkCapabilities.getNetworkSpecifier();
+ subId = telephonyNetworkSpecifier.getSubscriptionId();
+ } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+ && networkCapabilities.getTransportInfo() instanceof WifiInfo) {
+ WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
+ subId = mDeps.getSubIdForWifiInfo(wifiInfo);
+ }
+
+ boolean isVcnManagedNetwork = false;
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ synchronized (mLock) {
+ ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
+
+ // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
+ if (mVcns.containsKey(subGroup)) {
+ isVcnManagedNetwork = true;
+ }
+ }
+ }
+ if (isVcnManagedNetwork) {
+ networkCapabilities.removeCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
+ }
}
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
index eca1dfa..6a816af 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -22,12 +22,20 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.vibrator.IVibrator;
+import android.os.BatteryStats;
+import android.os.Binder;
import android.os.CombinedVibrationEffect;
+import android.os.ExternalVibration;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IExternalVibratorService;
import android.os.IVibratorManagerService;
+import android.os.IVibratorStateListener;
import android.os.Looper;
+import android.os.PowerManager;
+import android.os.Process;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.Trace;
@@ -37,12 +45,17 @@
import android.os.VibratorInfo;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.DumpUtils;
+import com.android.server.vibrator.InputDeviceDelegate;
import com.android.server.vibrator.Vibration;
import com.android.server.vibrator.VibrationScaler;
import com.android.server.vibrator.VibrationSettings;
+import com.android.server.vibrator.VibrationThread;
import com.android.server.vibrator.VibratorController;
import libcore.util.NativeAllocationRegistry;
@@ -50,7 +63,11 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -83,18 +100,32 @@
}
}
+ // Used to generate globally unique vibration ids.
+ private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback
+
private final Object mLock = new Object();
private final Context mContext;
+ private final PowerManager.WakeLock mWakeLock;
+ private final IBatteryStats mBatteryStatsService;
private final Handler mHandler;
private final AppOpsManager mAppOps;
private final NativeWrapper mNativeWrapper;
+ private final VibratorManagerRecords mVibratorManagerRecords;
private final int[] mVibratorIds;
private final SparseArray<VibratorController> mVibrators;
+ private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
@GuardedBy("mLock")
private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>();
+ @GuardedBy("mLock")
+ private VibrationThread mCurrentVibration;
+ @GuardedBy("mLock")
+ private VibrationThread mNextVibration;
+ @GuardedBy("mLock")
+ private ExternalVibrationHolder mCurrentExternalVibration;
private VibrationSettings mVibrationSettings;
private VibrationScaler mVibrationScaler;
+ private InputDeviceDelegate mInputDeviceDelegate;
static native long nativeInit();
@@ -109,8 +140,19 @@
mNativeWrapper = injector.getNativeWrapper();
mNativeWrapper.init();
+ int dumpLimit = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_previousVibrationsDumpLimit);
+ mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
+
+ mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME));
+
mAppOps = mContext.getSystemService(AppOpsManager.class);
+ PowerManager pm = context.getSystemService(PowerManager.class);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
+ mWakeLock.setReferenceCounted(true);
+
int[] vibratorIds = mNativeWrapper.getVibratorIds();
if (vibratorIds == null) {
mVibratorIds = new int[0];
@@ -134,6 +176,7 @@
try {
mVibrationSettings = new VibrationSettings(mContext, mHandler);
mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
+ mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler);
mVibrationSettings.addListener(this::updateServiceState);
@@ -145,6 +188,11 @@
}
@Override // Binder call
+ public int[] getVibratorIds() {
+ return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
+ }
+
+ @Override // Binder call
@Nullable
public VibratorInfo getVibratorInfo(int vibratorId) {
VibratorController controller = mVibrators.get(vibratorId);
@@ -152,8 +200,37 @@
}
@Override // Binder call
- public int[] getVibratorIds() {
- return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
+ public boolean isVibrating(int vibratorId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_VIBRATOR_STATE,
+ "isVibrating");
+ VibratorController controller = mVibrators.get(vibratorId);
+ return controller != null && controller.isVibrating();
+ }
+
+ @Override // Binder call
+ public boolean registerVibratorStateListener(int vibratorId, IVibratorStateListener listener) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_VIBRATOR_STATE,
+ "registerVibratorStateListener");
+ VibratorController controller = mVibrators.get(vibratorId);
+ if (controller == null) {
+ return false;
+ }
+ return controller.registerVibratorStateListener(listener);
+ }
+
+ @Override // Binder call
+ public boolean unregisterVibratorStateListener(int vibratorId,
+ IVibratorStateListener listener) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_VIBRATOR_STATE,
+ "unregisterVibratorStateListener");
+ VibratorController controller = mVibrators.get(vibratorId);
+ if (controller == null) {
+ return false;
+ }
+ return controller.unregisterVibratorStateListener(listener);
}
@Override // Binder call
@@ -161,9 +238,10 @@
@Nullable CombinedVibrationEffect effect, @Nullable VibrationAttributes attrs) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "setAlwaysOnEffect");
try {
- if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) {
- throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission");
- }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.VIBRATE_ALWAYS_ON,
+ "setAlwaysOnEffect");
+
if (effect == null) {
synchronized (mLock) {
mAlwaysOnEffects.delete(alwaysOnId);
@@ -200,12 +278,113 @@
@Override // Binder call
public void vibrate(int uid, String opPkg, @NonNull CombinedVibrationEffect effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
- throw new UnsupportedOperationException("Not implemented");
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
+ try {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.VIBRATE, "vibrate");
+
+ if (token == null) {
+ Slog.e(TAG, "token must not be null");
+ return;
+ }
+ enforceUpdateAppOpsStatsPermission(uid);
+ if (!isEffectValid(effect)) {
+ return;
+ }
+ effect = fixupVibrationEffect(effect);
+ attrs = fixupVibrationAttributes(attrs);
+ Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs,
+ uid, opPkg, reason);
+
+ synchronized (mLock) {
+ Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
+ if (ignoreStatus != null) {
+ endVibrationLocked(vib, ignoreStatus);
+ return;
+ }
+
+ VibrationThread vibThread = new VibrationThread(vib, mVibrators, mWakeLock,
+ mBatteryStatsService, mVibrationCallbacks);
+
+ ignoreStatus = shouldIgnoreVibrationForCurrentLocked(vibThread);
+ if (ignoreStatus != null) {
+ endVibrationLocked(vib, ignoreStatus);
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mCurrentVibration != null) {
+ mCurrentVibration.cancel();
+ }
+ Vibration.Status status = startVibrationLocked(vibThread);
+ if (status != Vibration.Status.RUNNING) {
+ endVibrationLocked(vib, status);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
}
@Override // Binder call
public void cancelVibrate(IBinder token) {
- throw new UnsupportedOperationException("Not implemented");
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "cancelVibrate");
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.VIBRATE,
+ "cancelVibrate");
+
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Canceling vibration.");
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mNextVibration = null;
+ if (mCurrentVibration != null
+ && mCurrentVibration.getVibration().token == token) {
+ mCurrentVibration.cancel();
+ }
+ if (mCurrentExternalVibration != null) {
+ mCurrentExternalVibration.end(Vibration.Status.CANCELLED);
+ mVibratorManagerRecords.record(mCurrentExternalVibration);
+ mCurrentExternalVibration.externalVibration.mute();
+ mCurrentExternalVibration = null;
+ // TODO(b/167946816): set external control to false
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+ final long ident = Binder.clearCallingIdentity();
+
+ boolean isDumpProto = false;
+ for (String arg : args) {
+ if (arg.equals("--proto")) {
+ isDumpProto = true;
+ }
+ }
+ try {
+ if (isDumpProto) {
+ mVibratorManagerRecords.dumpProto(fd);
+ } else {
+ mVibratorManagerRecords.dumpText(pw);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
@@ -214,11 +393,24 @@
new VibratorManagerShellCommand(this).exec(this, in, out, err, args, cb, resultReceiver);
}
- private void updateServiceState() {
+ @VisibleForTesting
+ void updateServiceState() {
synchronized (mLock) {
+ boolean inputDevicesChanged = mInputDeviceDelegate.updateInputDeviceVibrators(
+ mVibrationSettings.shouldVibrateInputDevices());
+
for (int i = 0; i < mAlwaysOnEffects.size(); i++) {
updateAlwaysOnLocked(mAlwaysOnEffects.valueAt(i));
}
+
+ if (mCurrentVibration == null) {
+ return;
+ }
+
+ if (inputDevicesChanged || !mVibrationSettings.shouldVibrateForPowerMode(
+ mCurrentVibration.getVibration().attrs.getUsage())) {
+ mCurrentVibration.cancel();
+ }
}
}
@@ -230,9 +422,9 @@
if (vibrator == null) {
continue;
}
- Vibration.Status ignoredStatus = shouldIgnoreVibrationLocked(
+ Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
vib.uid, vib.opPkg, vib.attrs);
- if (ignoredStatus == null) {
+ if (ignoreStatus == null) {
effect = mVibrationScaler.scale(effect, vib.attrs.getUsage());
} else {
// Vibration should not run, use null effect to remove registered effect.
@@ -242,6 +434,135 @@
}
}
+ @GuardedBy("mLock")
+ private Vibration.Status startVibrationLocked(VibrationThread vibThread) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
+ try {
+ Vibration vib = vibThread.getVibration();
+ vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
+
+ boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
+ vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
+
+ if (inputDevicesAvailable) {
+ return Vibration.Status.FORWARDED_TO_INPUT_DEVICES;
+ }
+
+ if (mCurrentVibration == null) {
+ return startVibrationThreadLocked(vibThread);
+ }
+
+ mNextVibration = vibThread;
+ return Vibration.Status.RUNNING;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private Vibration.Status startVibrationThreadLocked(VibrationThread vibThread) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationThreadLocked");
+ try {
+ Vibration vib = vibThread.getVibration();
+ int mode = startAppOpModeLocked(vib.uid, vib.opPkg, vib.attrs);
+ switch (mode) {
+ case AppOpsManager.MODE_ALLOWED:
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+ mCurrentVibration = vibThread;
+ mCurrentVibration.start();
+ return Vibration.Status.RUNNING;
+ case AppOpsManager.MODE_ERRORED:
+ Slog.w(TAG, "Start AppOpsManager operation errored for uid " + vib.uid);
+ return Vibration.Status.IGNORED_ERROR_APP_OPS;
+ default:
+ return Vibration.Status.IGNORED_APP_OPS;
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void endVibrationLocked(Vibration vib, Vibration.Status status) {
+ vib.end(status);
+ mVibratorManagerRecords.record(vib);
+ }
+
+ @GuardedBy("mLock")
+ private void reportFinishedVibrationLocked(Vibration.Status status) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+ try {
+ Vibration vib = mCurrentVibration.getVibration();
+ mCurrentVibration = null;
+ endVibrationLocked(vib, status);
+ finishAppOpModeLocked(vib.uid, vib.opPkg);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ private void onVibrationComplete(int vibratorId, long vibrationId) {
+ synchronized (mLock) {
+ if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId
+ + " complete, notifying thread");
+ }
+ mCurrentVibration.vibratorComplete(vibratorId);
+ }
+ }
+ }
+
+ /**
+ * Check if given vibration should be ignored in favour of one of the vibrations currently
+ * running on the same vibrators.
+ *
+ * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private Vibration.Status shouldIgnoreVibrationForCurrentLocked(VibrationThread vibThread) {
+ if (vibThread.getVibration().isRepeating()) {
+ // Repeating vibrations always take precedence.
+ return null;
+ }
+ if (mCurrentVibration != null && mCurrentVibration.getVibration().isRepeating()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration in favor of previous alarm vibration");
+ }
+ return Vibration.Status.IGNORED_FOR_ALARM;
+ }
+ return null;
+ }
+
+ /**
+ * Check if given vibration should be ignored by this service.
+ *
+ * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
+ * @see #shouldIgnoreVibrationLocked(int, String, VibrationAttributes)
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private Vibration.Status shouldIgnoreVibrationLocked(Vibration vib) {
+ // If something has external control of the vibrator, assume that it's more important.
+ if (mCurrentExternalVibration != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
+ }
+ return Vibration.Status.IGNORED_FOR_EXTERNAL;
+ }
+
+ if (!mVibrationSettings.shouldVibrateForUid(vib.uid, vib.attrs.getUsage())) {
+ Slog.e(TAG, "Ignoring incoming vibration as process with"
+ + " uid= " + vib.uid + " is background,"
+ + " attrs= " + vib.attrs);
+ return Vibration.Status.IGNORED_BACKGROUND;
+ }
+
+ return shouldIgnoreVibrationLocked(vib.uid, vib.opPkg, vib.attrs);
+ }
+
/**
* Check if a vibration with given {@code uid}, {@code opPkg} and {@code attrs} should be
* ignored by this service.
@@ -271,7 +592,7 @@
return Vibration.Status.IGNORED_RINGTONE;
}
- int mode = getAppOpMode(uid, opPkg, attrs);
+ int mode = checkAppOpModeLocked(uid, opPkg, attrs);
if (mode != AppOpsManager.MODE_ALLOWED) {
if (mode == AppOpsManager.MODE_ERRORED) {
// We might be getting calls from within system_server, so we don't actually
@@ -290,21 +611,49 @@
* Check which mode should be set for a vibration with given {@code uid}, {@code opPkg} and
* {@code attrs}. This will return one of the AppOpsManager.MODE_*.
*/
- private int getAppOpMode(int uid, String opPkg, VibrationAttributes attrs) {
+ @GuardedBy("mLock")
+ private int checkAppOpModeLocked(int uid, String opPkg, VibrationAttributes attrs) {
int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
attrs.getAudioUsage(), uid, opPkg);
- if (mode == AppOpsManager.MODE_ALLOWED) {
- mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, opPkg);
- }
- if (mode == AppOpsManager.MODE_IGNORED
- && attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
+ int fixedMode = fixupAppOpModeLocked(mode, attrs);
+ if (mode != fixedMode && fixedMode == AppOpsManager.MODE_ALLOWED) {
// If we're just ignoring the vibration op then this is set by DND and we should ignore
// if we're asked to bypass. AppOps won't be able to record this operation, so make
// sure we at least note it in the logs for debugging.
Slog.d(TAG, "Bypassing DND for vibrate from uid " + uid);
- mode = AppOpsManager.MODE_ALLOWED;
}
- return mode;
+ return fixedMode;
+ }
+
+ /** Start an operation in {@link AppOpsManager}, if allowed. */
+ @GuardedBy("mLock")
+ private int startAppOpModeLocked(int uid, String opPkg, VibrationAttributes attrs) {
+ return fixupAppOpModeLocked(
+ mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, opPkg), attrs);
+ }
+
+ /**
+ * Finish a previously started operation in {@link AppOpsManager}. This will be a noop if no
+ * operation with same uid was previously started.
+ */
+ @GuardedBy("mLock")
+ private void finishAppOpModeLocked(int uid, String opPkg) {
+ mAppOps.finishOp(AppOpsManager.OP_VIBRATE, uid, opPkg);
+ }
+
+ /**
+ * Enforces {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} to incoming UID if it's
+ * different from the calling UID.
+ */
+ private void enforceUpdateAppOpsStatsPermission(int uid) {
+ if (uid == Binder.getCallingUid()) {
+ return;
+ }
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
}
/**
@@ -330,6 +679,49 @@
}
/**
+ * Sets fallback effects to all prebaked ones in given combination of effects, based on {@link
+ * VibrationSettings#getFallbackEffect}.
+ */
+ private CombinedVibrationEffect fixupVibrationEffect(CombinedVibrationEffect effect) {
+ if (effect instanceof CombinedVibrationEffect.Mono) {
+ return CombinedVibrationEffect.createSynced(
+ fixupVibrationEffect(((CombinedVibrationEffect.Mono) effect).getEffect()));
+ } else if (effect instanceof CombinedVibrationEffect.Stereo) {
+ CombinedVibrationEffect.SyncedCombination combination =
+ CombinedVibrationEffect.startSynced();
+ SparseArray<VibrationEffect> effects =
+ ((CombinedVibrationEffect.Stereo) effect).getEffects();
+ for (int i = 0; i < effects.size(); i++) {
+ combination.addVibrator(effects.keyAt(i), fixupVibrationEffect(effects.valueAt(i)));
+ }
+ return combination.combine();
+ } else if (effect instanceof CombinedVibrationEffect.Sequential) {
+ CombinedVibrationEffect.SequentialCombination combination =
+ CombinedVibrationEffect.startSequential();
+ List<CombinedVibrationEffect> effects =
+ ((CombinedVibrationEffect.Sequential) effect).getEffects();
+ for (CombinedVibrationEffect e : effects) {
+ combination.addNext(fixupVibrationEffect(e));
+ }
+ return combination.combine();
+ }
+ return effect;
+ }
+
+ private VibrationEffect fixupVibrationEffect(VibrationEffect effect) {
+ if (effect instanceof VibrationEffect.Prebaked
+ && ((VibrationEffect.Prebaked) effect).shouldFallback()) {
+ VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
+ VibrationEffect fallback = mVibrationSettings.getFallbackEffect(prebaked.getId());
+ if (fallback != null) {
+ return new VibrationEffect.Prebaked(prebaked.getId(), prebaked.getEffectStrength(),
+ fallback);
+ }
+ }
+ return effect;
+ }
+
+ /**
* Return new {@link VibrationAttributes} that only applies flags that this user has permissions
* to use.
*/
@@ -388,6 +780,19 @@
}
}
+ /**
+ * Check given mode, one of the AppOpsManager.MODE_*, against {@link VibrationAttributes} to
+ * allow bypassing {@link AppOpsManager} checks.
+ */
+ @GuardedBy("mLock")
+ private int fixupAppOpModeLocked(int mode, VibrationAttributes attrs) {
+ if (mode == AppOpsManager.MODE_IGNORED
+ && attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
+ return AppOpsManager.MODE_ALLOWED;
+ }
+ return mode;
+ }
+
private boolean hasPermission(String permission) {
return mContext.checkCallingOrSelfPermission(permission)
== PackageManager.PERMISSION_GRANTED;
@@ -428,6 +833,42 @@
}
/**
+ * Implementation of {@link VibrationThread.VibrationCallbacks} that controls synced vibrations
+ * and reports them when finished.
+ */
+ private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
+
+ @Override
+ public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) {
+ // TODO(b/167946816): call IVibratorManager to prepare
+ }
+
+ @Override
+ public void triggerSyncedVibration(long vibrationId) {
+ // TODO(b/167946816): call IVibratorManager to trigger
+ }
+
+ @Override
+ public void onVibrationEnded(long vibrationId, Vibration.Status status) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration " + vibrationId + " thread finished with status " + status);
+ }
+ synchronized (mLock) {
+ if (mCurrentVibration != null
+ && mCurrentVibration.getVibration().id == vibrationId) {
+ reportFinishedVibrationLocked(status);
+
+ if (mNextVibration != null) {
+ VibrationThread vibThread = mNextVibration;
+ mNextVibration = null;
+ startVibrationThreadLocked(vibThread);
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Implementation of {@link VibratorController.OnVibrationCompleteListener} with a weak
* reference to this service.
*/
@@ -443,7 +884,7 @@
public void onComplete(int vibratorId, long vibrationId) {
VibratorManagerService service = mServiceRef.get();
if (service != null) {
- // TODO(b/159207608): finish vibration if all vibrators finished for this vibration
+ service.onVibrationComplete(vibratorId, vibrationId);
}
}
}
@@ -469,6 +910,41 @@
}
}
+ /** Holder for a {@link ExternalVibration}. */
+ private final class ExternalVibrationHolder {
+
+ public final ExternalVibration externalVibration;
+ public int scale;
+
+ private final long mStartTimeDebug;
+ private long mEndTimeDebug;
+ private Vibration.Status mStatus;
+
+ private ExternalVibrationHolder(ExternalVibration externalVibration) {
+ this.externalVibration = externalVibration;
+ this.scale = IExternalVibratorService.SCALE_NONE;
+ mStartTimeDebug = System.currentTimeMillis();
+ mStatus = Vibration.Status.RUNNING;
+ }
+
+ public void end(Vibration.Status status) {
+ if (mStatus != Vibration.Status.RUNNING) {
+ // Vibration already ended, keep first ending status set and ignore this one.
+ return;
+ }
+ mStatus = status;
+ mEndTimeDebug = System.currentTimeMillis();
+ }
+
+ public Vibration.DebugInfo getDebugInfo() {
+ return new Vibration.DebugInfo(
+ mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
+ scale, externalVibration.getVibrationAttributes(),
+ externalVibration.getUid(), externalVibration.getPackage(),
+ /* reason= */ null, mStatus);
+ }
+ }
+
/** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */
@VisibleForTesting
public static class NativeWrapper {
@@ -494,8 +970,158 @@
}
}
+ /** Keep records of vibrations played and provide debug information for this service. */
+ private final class VibratorManagerRecords {
+ @GuardedBy("mLock")
+ private final SparseArray<LinkedList<Vibration.DebugInfo>> mPreviousVibrations =
+ new SparseArray<>();
+ @GuardedBy("mLock")
+ private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations =
+ new LinkedList<>();
+ private final int mPreviousVibrationsLimit;
+
+ private VibratorManagerRecords(int limit) {
+ mPreviousVibrationsLimit = limit;
+ }
+
+ @GuardedBy("mLock")
+ void record(Vibration vib) {
+ int usage = vib.attrs.getUsage();
+ if (!mPreviousVibrations.contains(usage)) {
+ mPreviousVibrations.put(usage, new LinkedList<>());
+ }
+ record(mPreviousVibrations.get(usage), vib.getDebugInfo());
+ }
+
+ @GuardedBy("mLock")
+ void record(ExternalVibrationHolder vib) {
+ record(mPreviousExternalVibrations, vib.getDebugInfo());
+ }
+
+ @GuardedBy("mLock")
+ void record(LinkedList<Vibration.DebugInfo> records, Vibration.DebugInfo info) {
+ if (records.size() > mPreviousVibrationsLimit) {
+ records.removeFirst();
+ }
+ records.addLast(info);
+ }
+
+ void dumpText(PrintWriter pw) {
+ pw.println("Vibrator Manager Service:");
+ synchronized (mLock) {
+ pw.println(" mVibratorControllers:");
+ for (int i = 0; i < mVibrators.size(); i++) {
+ pw.println(" " + mVibrators.valueAt(i));
+ }
+ pw.println();
+ pw.println(" mCurrentVibration:");
+ pw.println(" " + mCurrentVibration == null
+ ? null : mCurrentVibration.getVibration().getDebugInfo());
+ pw.println(" mNextVibration:");
+ pw.println(" " + mNextVibration == null
+ ? null : mNextVibration.getVibration().getDebugInfo());
+ pw.println(" mCurrentExternalVibration:");
+ pw.println(" " + mCurrentExternalVibration == null
+ ? null : mCurrentExternalVibration.getDebugInfo());
+ pw.println();
+ pw.println(" mVibrationSettings=" + mVibrationSettings);
+ for (int i = 0; i < mPreviousVibrations.size(); i++) {
+ pw.println();
+ pw.print(" Previous vibrations for usage ");
+ pw.print(VibrationAttributes.usageToString(mPreviousVibrations.keyAt(i)));
+ pw.println(":");
+ for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
+ pw.println(" " + info);
+ }
+ }
+
+ pw.println(" Previous external vibrations:");
+ for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
+ pw.println(" " + info);
+ }
+ }
+ }
+
+ synchronized void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ synchronized (mLock) {
+ mVibrationSettings.dumpProto(proto);
+ if (mCurrentVibration != null) {
+ mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
+ VibratorServiceDumpProto.CURRENT_VIBRATION);
+ }
+ if (mCurrentExternalVibration != null) {
+ mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
+ VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
+ }
+
+ boolean isVibrating = false;
+ boolean isUnderExternalControl = false;
+ for (int i = 0; i < mVibrators.size(); i++) {
+ isVibrating |= mVibrators.valueAt(i).isVibrating();
+ isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl();
+ }
+ proto.write(VibratorServiceDumpProto.IS_VIBRATING, isVibrating);
+ proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
+ isUnderExternalControl);
+
+ for (Vibration.DebugInfo info : mPreviousVibrations.get(
+ VibrationAttributes.USAGE_RINGTONE)) {
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
+ }
+
+ for (Vibration.DebugInfo info : mPreviousVibrations.get(
+ VibrationAttributes.USAGE_NOTIFICATION)) {
+ info.dumpProto(proto,
+ VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
+ }
+
+ for (Vibration.DebugInfo info : mPreviousVibrations.get(
+ VibrationAttributes.USAGE_ALARM)) {
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
+ }
+
+ for (Vibration.DebugInfo info : mPreviousVibrations.get(
+ VibrationAttributes.USAGE_UNKNOWN)) {
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+ }
+
+ for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+ }
+ }
+ proto.flush();
+ }
+ }
+
/** Provide limited functionality from {@link VibratorManagerService} as shell commands. */
private final class VibratorManagerShellCommand extends ShellCommand {
+ public static final String SHELL_PACKAGE_NAME = "com.android.shell";
+
+ private final class CommonOptions {
+ public boolean force = false;
+ public String description = "Shell command";
+
+ CommonOptions() {
+ String nextArg;
+ while ((nextArg = peekNextArg()) != null) {
+ switch (nextArg) {
+ case "-f":
+ getNextArgRequired(); // consume the -f argument;
+ force = true;
+ break;
+ case "-d":
+ getNextArgRequired(); // consume the -d argument;
+ description = getNextArgRequired();
+ break;
+ default:
+ // Not a common option, finish reading.
+ return;
+ }
+ }
+ }
+ }
private final IBinder mToken;
@@ -505,10 +1131,27 @@
@Override
public int onCommand(String cmd) {
- if ("list".equals(cmd)) {
- return runListVibrators();
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onCommand " + cmd);
+ try {
+ if ("list".equals(cmd)) {
+ return runListVibrators();
+ }
+ if ("synced".equals(cmd)) {
+ return runMono();
+ }
+ if ("combined".equals(cmd)) {
+ return runStereo();
+ }
+ if ("sequential".equals(cmd)) {
+ return runSequential();
+ }
+ if ("cancel".equals(cmd)) {
+ return runCancel();
+ }
+ return handleDefaultCommands(cmd);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
- return handleDefaultCommands(cmd);
}
private int runListVibrators() {
@@ -525,6 +1168,157 @@
}
}
+ private int runMono() {
+ CommonOptions commonOptions = new CommonOptions();
+ VibrationEffect effect = nextEffect();
+ if (effect == null) {
+ return 0;
+ }
+
+ CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect);
+ VibrationAttributes attrs = createVibrationAttributes(commonOptions);
+ vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combinedEffect, attrs,
+ commonOptions.description, mToken);
+ return 0;
+ }
+
+ private int runStereo() {
+ CommonOptions commonOptions = new CommonOptions();
+ CombinedVibrationEffect.SyncedCombination combination =
+ CombinedVibrationEffect.startSynced();
+ while ("-v".equals(getNextOption())) {
+ int vibratorId = Integer.parseInt(getNextArgRequired());
+ VibrationEffect effect = nextEffect();
+ if (effect != null) {
+ combination.addVibrator(vibratorId, effect);
+ }
+ }
+ VibrationAttributes attrs = createVibrationAttributes(commonOptions);
+ vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
+ commonOptions.description, mToken);
+ return 0;
+ }
+
+ private int runSequential() {
+ CommonOptions commonOptions = new CommonOptions();
+
+ CombinedVibrationEffect.SequentialCombination combination =
+ CombinedVibrationEffect.startSequential();
+ while ("-v".equals(getNextOption())) {
+ int vibratorId = Integer.parseInt(getNextArgRequired());
+ int delay = 0;
+ if ("-w".equals(getNextOption())) {
+ delay = Integer.parseInt(getNextArgRequired());
+ }
+ VibrationEffect effect = nextEffect();
+ if (effect != null) {
+ combination.addNext(vibratorId, effect, delay);
+ }
+ }
+ VibrationAttributes attrs = createVibrationAttributes(commonOptions);
+ vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
+ commonOptions.description, mToken);
+ return 0;
+ }
+
+ private int runCancel() {
+ cancelVibrate(mToken);
+ return 0;
+ }
+
+ @Nullable
+ private VibrationEffect nextEffect() {
+ String effectType = getNextArgRequired();
+ if ("oneshot".equals(effectType)) {
+ return nextOneShot();
+ }
+ if ("waveform".equals(effectType)) {
+ return nextWaveform();
+ }
+ if ("prebaked".equals(effectType)) {
+ return nextPrebaked();
+ }
+ if ("composed".equals(effectType)) {
+ return nextComposed();
+ }
+ return null;
+ }
+
+ private VibrationEffect nextOneShot() {
+ boolean hasAmplitude = "-a".equals(getNextOption());
+ long duration = Long.parseLong(getNextArgRequired());
+ int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired())
+ : VibrationEffect.DEFAULT_AMPLITUDE;
+ return VibrationEffect.createOneShot(duration, amplitude);
+ }
+
+ private VibrationEffect nextWaveform() {
+ boolean hasAmplitudes = false;
+ int repeat = -1;
+
+ String nextOption = getNextOption();
+ while (nextOption != null) {
+ if ("-a".equals(nextOption)) {
+ hasAmplitudes = true;
+ } else if ("-r".equals(nextOption)) {
+ repeat = Integer.parseInt(getNextArgRequired());
+ }
+ nextOption = getNextOption();
+ }
+ List<Long> durations = new ArrayList<>();
+ List<Integer> amplitudes = new ArrayList<>();
+
+ String nextArg;
+ while ((nextArg = peekNextArg()) != null && !"-v".equals(nextArg)) {
+ durations.add(Long.parseLong(getNextArgRequired()));
+ if (hasAmplitudes) {
+ amplitudes.add(Integer.parseInt(getNextArgRequired()));
+ }
+ }
+
+ long[] durationArray = durations.stream().mapToLong(Long::longValue).toArray();
+ if (!hasAmplitudes) {
+ return VibrationEffect.createWaveform(durationArray, repeat);
+ }
+
+ int[] amplitudeArray = amplitudes.stream().mapToInt(Integer::intValue).toArray();
+ return VibrationEffect.createWaveform(durationArray, amplitudeArray, repeat);
+ }
+
+ private VibrationEffect nextPrebaked() {
+ boolean shouldFallback = "-b".equals(getNextOption());
+ int effectId = Integer.parseInt(getNextArgRequired());
+ return VibrationEffect.get(effectId, shouldFallback);
+ }
+
+ private VibrationEffect nextComposed() {
+ VibrationEffect.Composition composition = VibrationEffect.startComposition();
+ String nextArg;
+ while ((nextArg = peekNextArg()) != null) {
+ int delay = 0;
+ if ("-w".equals(nextArg)) {
+ getNextArgRequired(); // consume the -w option
+ delay = Integer.parseInt(getNextArgRequired());
+ } else if ("-v".equals(nextArg)) {
+ // Starting next vibrator, this composed effect if finished.
+ break;
+ }
+ int primitiveId = Integer.parseInt(getNextArgRequired());
+ composition.addPrimitive(primitiveId, /* scale= */ 1f, delay);
+ }
+ return composition.compose();
+ }
+
+ private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) {
+ final int flags =
+ commonOptions.force ? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY : 0;
+ return new VibrationAttributes.Builder()
+ .setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
+ // Used to apply Settings.System.HAPTIC_FEEDBACK_INTENSITY to scale effects.
+ .setUsage(VibrationAttributes.USAGE_TOUCH)
+ .build();
+ }
+
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter();) {
@@ -535,6 +1329,49 @@
pw.println(" list");
pw.println(" Prints the id of device vibrators. This does not include any ");
pw.println(" connected input device.");
+ pw.println(" synced [options] <effect>");
+ pw.println(" Vibrates effect on all vibrators in sync.");
+ pw.println(" combined [options] (-v <vibrator-id> <effect>)...");
+ pw.println(" Vibrates different effects on each vibrator in sync.");
+ pw.println(" sequential [options] (-v <vibrator-id> [-w <delay>] <effect>)...");
+ pw.println(" Vibrates different effects on each vibrator in sequence.");
+ pw.println(" cancel");
+ pw.println(" Cancels any active vibration");
+ pw.println("");
+ pw.println("Effect commands:");
+ pw.println(" oneshot [-a] <duration> [<amplitude>]");
+ pw.println(" Vibrates for duration milliseconds; ignored when device is on ");
+ pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting ");
+ pw.println(" will be used to scale amplitude.");
+ pw.println(" If -a is provided, the command accepts a second argument for ");
+ pw.println(" amplitude, in a scale of 1-255.");
+ pw.println(" waveform [-r <index>] [-a] (<duration> [<amplitude>])...");
+ pw.println(" Vibrates for durations and amplitudes in list; ignored when ");
+ pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength ");
+ pw.println(" user setting will be used to scale amplitude.");
+ pw.println(" If -r is provided, the waveform loops back to the specified");
+ pw.println(" index (e.g. 0 loops from the beginning)");
+ pw.println(" If -a is provided, the command accepts duration-amplitude pairs;");
+ pw.println(" otherwise, it accepts durations only and alternates off/on");
+ pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255.");
+ pw.println(" prebaked [-b] <effect-id>");
+ pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
+ pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
+ pw.println(" will be used to scale amplitude.");
+ pw.println(" If -b is provided, the prebaked fallback effect will be played if");
+ pw.println(" the device doesn't support the given effect-id.");
+ pw.println(" composed [-w <delay>] <primitive-id>...");
+ pw.println(" Vibrates with a composed effect; ignored when device is on DND ");
+ pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
+ pw.println(" will be used to scale primitive intensities.");
+ pw.println(" If -w is provided, the next primitive will be played after the ");
+ pw.println(" specified wait time in milliseconds.");
+ pw.println("");
+ pw.println("Common Options:");
+ pw.println(" -f");
+ pw.println(" Force. Ignore Do Not Disturb setting.");
+ pw.println(" -d <description>");
+ pw.println(" Add description to the vibration.");
pw.println("");
}
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 97e313e..026eb63 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -772,18 +772,7 @@
proto.write(VibratorServiceDumpProto.IS_VIBRATING, mVibratorController.isVibrating());
proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
mVibratorController.isUnderExternalControl());
- proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH));
- proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
- proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- proto.write(VibratorServiceDumpProto.RING_INTENSITY,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
- proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ mVibrationSettings.dumpProto(proto);
for (Vibration.DebugInfo info : mPreviousRingVibrations) {
info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index efe82df..5b50431 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -31,6 +31,7 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceDebugInfo;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -110,7 +111,6 @@
};
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
- "android.hardware.audio@2.0::IDevicesFactory",
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
"android.hardware.audio@6.0::IDevicesFactory",
@@ -136,6 +136,11 @@
"android.system.suspend@1.0::ISystemSuspend"
);
+ public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
+ "android.hardware.light.ILights/",
+ "android.hardware.power.stats.IPowerStats/",
+ };
+
private static Watchdog sWatchdog;
private final Thread mThread;
@@ -527,12 +532,11 @@
return builder.toString();
}
- private static ArrayList<Integer> getInterestingHalPids() {
+ private static void addInterestingHidlPids(HashSet<Integer> pids) {
try {
IServiceManager serviceManager = IServiceManager.getService();
ArrayList<IServiceManager.InstanceDebugInfo> dump =
serviceManager.debugDump();
- HashSet<Integer> pids = new HashSet<>();
for (IServiceManager.InstanceDebugInfo info : dump) {
if (info.pid == IServiceManager.PidConstant.NO_PID) {
continue;
@@ -544,24 +548,37 @@
pids.add(info.pid);
}
- return new ArrayList<Integer>(pids);
} catch (RemoteException e) {
- return new ArrayList<Integer>();
+ Log.w(TAG, e);
+ }
+ }
+
+ private static void addInterestingAidlPids(HashSet<Integer> pids) {
+ ServiceDebugInfo[] infos = ServiceManager.getServiceDebugInfo();
+ if (infos == null) return;
+
+ for (ServiceDebugInfo info : infos) {
+ for (String prefix : AIDL_INTERFACE_PREFIXES_OF_INTEREST) {
+ if (info.name.startsWith(prefix)) {
+ pids.add(info.debugPid);
+ }
+ }
}
}
static ArrayList<Integer> getInterestingNativePids() {
- ArrayList<Integer> pids = getInterestingHalPids();
+ HashSet<Integer> pids = new HashSet<>();
+ addInterestingAidlPids(pids);
+ addInterestingHidlPids(pids);
int[] nativePids = Process.getPidsForCommands(NATIVE_STACKS_OF_INTEREST);
if (nativePids != null) {
- pids.ensureCapacity(pids.size() + nativePids.length);
for (int i : nativePids) {
pids.add(i);
}
}
- return pids;
+ return new ArrayList<Integer>(pids);
}
private void run() {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e476ca9..15afb98 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -671,34 +671,29 @@
}
if (fgRequired) {
- if (isFgsBgStart(r.mAllowStartForeground)) {
- if (!r.mLoggedInfoAllowStartForeground) {
- Slog.wtf(TAG, "Background started FGS " + r.mInfoAllowStartForeground);
- r.mLoggedInfoAllowStartForeground = true;
+ logFgsBackgroundStart(r);
+ if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
+ String msg = "startForegroundService() not allowed due to "
+ + "mAllowStartForeground false: service "
+ + r.shortInstanceName;
+ Slog.w(TAG, msg);
+ showFgsBgRestrictedNotificationLocked(r);
+ ApplicationInfo aInfo = null;
+ try {
+ aInfo = AppGlobals.getPackageManager().getApplicationInfo(
+ callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
+ userId);
+ } catch (android.os.RemoteException e) {
+ // pm is in same process, this will never happen.
}
- if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
- String msg = "startForegroundService() not allowed due to "
- + "mAllowStartForeground false: service "
- + r.shortInstanceName;
- Slog.w(TAG, msg);
- showFgsBgRestrictedNotificationLocked(r);
- ApplicationInfo aInfo = null;
- try {
- aInfo = AppGlobals.getPackageManager().getApplicationInfo(
- callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
- userId);
- } catch (android.os.RemoteException e) {
- // pm is in same process, this will never happen.
- }
- if (aInfo == null) {
- throw new SecurityException("startServiceLocked failed, "
- + "could not resolve client package " + callingPackage);
- }
- if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
- throw new IllegalStateException(msg);
- }
- return null;
+ if (aInfo == null) {
+ throw new SecurityException("startServiceLocked failed, "
+ + "could not resolve client package " + callingPackage);
}
+ if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
+ throw new IllegalStateException(msg);
+ }
+ return null;
}
}
@@ -900,7 +895,7 @@
String callingPackage, @Nullable String callingFeatureId, int callingUid,
Intent service, boolean callerFg, final int userId,
final boolean isBinding, final IServiceConnection connection) {
- if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+ if (mAm.getPackageManagerInternal().isPermissionsReviewRequired(
r.packageName, r.userId)) {
// Show a permission review UI only for starting/binding from a foreground app
@@ -932,7 +927,7 @@
// binding request is still valid, so hook them up. We
// proceed only if the caller cleared the review requirement
// otherwise we unbind because the user didn't approve.
- if (!mAm.getPackageManagerInternalLocked()
+ if (!mAm.getPackageManagerInternal()
.isPermissionsReviewRequired(r.packageName,
r.userId)) {
try {
@@ -941,9 +936,14 @@
callerFg,
false /* whileRestarting */,
false /* permissionsReviewRequired */,
- false /* packageFrozen */);
+ false /* packageFrozen */,
+ true /* enqueueOomAdj */);
} catch (RemoteException e) {
/* ignore - local call */
+ } finally {
+ /* Will be a no-op if nothing pending */
+ mAm.updateOomAdjPendingTargetsLocked(
+ OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
}
} else {
unbindServiceLocked(connection);
@@ -994,7 +994,7 @@
int callingUid, int callingPid, boolean fgRequired, boolean callerFg, int userId,
boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken,
boolean isBinding, IServiceConnection connection) {
- final PackageManagerInternal pm = mAm.getPackageManagerInternalLocked();
+ final PackageManagerInternal pm = mAm.getPackageManagerInternal();
final boolean frozen = pm.isPackageFrozen(s.packageName, callingUid, s.userId);
if (!frozen) {
// Not frozen, it's okay to go
@@ -1025,9 +1025,14 @@
bringUpServiceLocked(s, serviceIntent.getFlags(), callerFg,
false /* whileRestarting */,
false /* permissionsReviewRequired */,
- false /* packageFrozen */);
+ false /* packageFrozen */,
+ true /* enqueueOomAdj */);
} catch (TransactionTooLargeException e) {
/* ignore - local call */
+ } finally {
+ /* Will be a no-op if nothing pending */
+ mAm.updateOomAdjPendingTargetsLocked(
+ OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
}
} else { // Starting a service
try {
@@ -1091,7 +1096,13 @@
FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START);
mAm.mBatteryStatsService.noteServiceStartRunning(uid, packageName, serviceName);
- String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false, false);
+ String error = bringUpServiceLocked(r, service.getFlags(), callerFg,
+ false /* whileRestarting */,
+ false /* permissionsReviewRequired */,
+ false /* packageFrozen */,
+ true /* enqueueOomAdj */);
+ /* Will be a no-op if nothing pending */
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
if (error != null) {
return new ComponentName("!!", error);
}
@@ -1117,7 +1128,7 @@
return r.name;
}
- private void stopServiceLocked(ServiceRecord service) {
+ private void stopServiceLocked(ServiceRecord service, boolean enqueueOomAdj) {
if (service.delayed) {
// If service isn't actually running, but is being held in the
// delayed list, then we need to keep it started but note that it
@@ -1140,7 +1151,7 @@
}
service.callStart = false;
- bringDownServiceIfNeededLocked(service, false, false);
+ bringDownServiceIfNeededLocked(service, false, false, enqueueOomAdj);
}
int stopServiceLocked(IApplicationThread caller, Intent service,
@@ -1163,7 +1174,7 @@
if (r.record != null) {
final long origId = Binder.clearCallingIdentity();
try {
- stopServiceLocked(r.record);
+ stopServiceLocked(r.record, false);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -1213,11 +1224,15 @@
}
}
if (stopping != null) {
- for (int i=stopping.size()-1; i>=0; i--) {
+ final int size = stopping.size();
+ for (int i = size - 1; i >= 0; i--) {
ServiceRecord service = stopping.get(i);
service.delayed = false;
services.ensureNotStartingBackgroundLocked(service);
- stopServiceLocked(service);
+ stopServiceLocked(service, true);
+ }
+ if (size > 0) {
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
}
}
}
@@ -1226,7 +1241,7 @@
void killMisbehavingService(ServiceRecord r,
int appUid, int appPid, String localPackageName) {
synchronized (mAm) {
- stopServiceLocked(r);
+ stopServiceLocked(r, false);
mAm.crashApplication(appUid, appPid, localPackageName, -1,
"Bad notification for startForeground", true /*force*/);
}
@@ -1301,7 +1316,7 @@
}
r.callStart = false;
final long origId = Binder.clearCallingIdentity();
- bringDownServiceIfNeededLocked(r, false, false);
+ bringDownServiceIfNeededLocked(r, false, false, false);
Binder.restoreCallingIdentity(origId);
return true;
}
@@ -1748,25 +1763,19 @@
}
if (!ignoreForeground) {
- if (isFgsBgStart(r.mAllowStartForeground)) {
- if (!r.mLoggedInfoAllowStartForeground) {
- Slog.wtf(TAG, "Background started FGS "
- + r.mInfoAllowStartForeground);
- r.mLoggedInfoAllowStartForeground = true;
- }
- if (r.mAllowStartForeground == FGS_FEATURE_DENIED
- && isBgFgsRestrictionEnabled(r)) {
- final String msg = "Service.startForeground() not allowed due to "
- + "mAllowStartForeground false: service "
- + r.shortInstanceName;
- Slog.w(TAG, msg);
- showFgsBgRestrictedNotificationLocked(r);
- updateServiceForegroundLocked(r.app, true);
- ignoreForeground = true;
- if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
- r.appInfo.uid)) {
- throw new IllegalStateException(msg);
- }
+ logFgsBackgroundStart(r);
+ if (r.mAllowStartForeground == FGS_FEATURE_DENIED
+ && isBgFgsRestrictionEnabled(r)) {
+ final String msg = "Service.startForeground() not allowed due to "
+ + "mAllowStartForeground false: service "
+ + r.shortInstanceName;
+ Slog.w(TAG, msg);
+ showFgsBgRestrictedNotificationLocked(r);
+ updateServiceForegroundLocked(r.app, true);
+ ignoreForeground = true;
+ if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
+ r.appInfo.uid)) {
+ throw new IllegalStateException(msg);
}
}
}
@@ -2229,12 +2238,12 @@
mAm.updateProcessForegroundLocked(proc, anyForeground, fgServiceTypes, oomAdj);
}
- private void updateWhitelistManagerLocked(ProcessRecord proc) {
- proc.whitelistManager = false;
+ private void updateAllowlistManagerLocked(ProcessRecord proc) {
+ proc.mAllowlistManager = false;
for (int i = proc.numberOfRunningServices() - 1; i >= 0; i--) {
ServiceRecord sr = proc.getRunningServiceAt(i);
if (sr.whitelistManager) {
- proc.whitelistManager = true;
+ proc.mAllowlistManager = true;
break;
}
}
@@ -2475,10 +2484,13 @@
}
clist.add(c);
+ boolean needOomAdj = false;
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
+ needOomAdj = true;
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
- permissionsReviewRequired, packageFrozen) != null) {
+ permissionsReviewRequired, packageFrozen, true) != null) {
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
return 0;
}
}
@@ -2489,7 +2501,7 @@
s.app.treatLikeActivity = true;
}
if (s.whitelistManager) {
- s.app.whitelistManager = true;
+ s.app.mAllowlistManager = true;
}
// This could have made the service more important.
mAm.updateLruProcessLocked(s.app,
@@ -2497,7 +2509,11 @@
|| (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
&& (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
b.client);
- mAm.updateOomAdjLocked(s.app, OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
+ needOomAdj = true;
+ mAm.enqueueOomAdjTargetLocked(s.app);
+ }
+ if (needOomAdj) {
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b
@@ -2591,7 +2607,7 @@
}
}
- serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
+ serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false, false);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2647,7 +2663,7 @@
try {
while (clist.size() > 0) {
ConnectionRecord r = clist.get(0);
- removeConnectionLocked(r, null, null);
+ removeConnectionLocked(r, null, null, true);
if (clist.size() > 0 && clist.get(0) == r) {
// In case it didn't get removed above, do it now.
Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);
@@ -2656,8 +2672,8 @@
final ProcessRecord app = r.binding.service.app;
if (app != null) {
- if (app.whitelistManager) {
- updateWhitelistManagerLocked(app);
+ if (app.mAllowlistManager) {
+ updateAllowlistManagerLocked(app);
}
// This could have made the service less important.
if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
@@ -2716,7 +2732,7 @@
}
}
- serviceDoneExecutingLocked(r, inDestroying, false);
+ serviceDoneExecutingLocked(r, inDestroying, false, false);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2788,12 +2804,24 @@
r = smap.mServicesByIntent.get(filter);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by intent: " + r);
}
- if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
- && !callingPackage.equals(r.packageName)) {
- // If an external service is running within its own package, other packages
- // should not bind to that instance.
- r = null;
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Whoops, can't use existing external service");
+ if (r != null) {
+ // Compared to resolveService below, the ServiceRecord here is retrieved from
+ // ServiceMap so the package visibility doesn't apply to it. We need to filter it.
+ if (mAm.getPackageManagerInternal().filterAppAccess(r.packageName, callingUid,
+ userId)) {
+ Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId
+ + ": not found");
+ return null;
+ }
+ if ((r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
+ && !callingPackage.equals(r.packageName)) {
+ // If an external service is running within its own package, other packages
+ // should not bind to that instance.
+ r = null;
+ if (DEBUG_SERVICE) {
+ Slog.v(TAG_SERVICE, "Whoops, can't use existing external service");
+ }
+ }
}
if (r == null) {
try {
@@ -2803,7 +2831,7 @@
flags |= PackageManager.MATCH_INSTANT;
}
// TODO: come back and remove this assumption to triage all services
- ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service,
+ ResolveInfo rInfo = mAm.getPackageManagerInternal().resolveService(service,
resolvedType, flags, userId, callingUid);
ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null;
if (sInfo == null) {
@@ -2876,7 +2904,7 @@
final long token = Binder.clearCallingIdentity();
try {
ResolveInfo rInfoForUserId0 =
- mAm.getPackageManagerInternalLocked().resolveService(service,
+ mAm.getPackageManagerInternal().resolveService(service,
resolvedType, flags, userId, callingUid);
if (rInfoForUserId0 == null) {
Slog.w(TAG_SERVICE,
@@ -3055,13 +3083,13 @@
// Keep the executeNesting count accurate.
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
final boolean inDestroying = mDestroyingServices.contains(r);
- serviceDoneExecutingLocked(r, inDestroying, inDestroying);
+ serviceDoneExecutingLocked(r, inDestroying, inDestroying, false);
throw e;
} catch (RemoteException e) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
- serviceDoneExecutingLocked(r, inDestroying, inDestroying);
+ serviceDoneExecutingLocked(r, inDestroying, inDestroying, false);
return false;
}
}
@@ -3217,9 +3245,12 @@
}
try {
bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false,
- false);
+ false, true);
} catch (TransactionTooLargeException e) {
// Ignore, it's been logged and nothing upstack cares.
+ } finally {
+ /* Will be a no-op if nothing pending */
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
}
}
@@ -3262,7 +3293,8 @@
}
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
- boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen)
+ boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
+ boolean enqueueOomAdj)
throws TransactionTooLargeException {
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
@@ -3299,7 +3331,7 @@
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": user " + r.userId + " is stopped";
Slog.w(TAG, msg);
- bringDownServiceLocked(r);
+ bringDownServiceLocked(r, enqueueOomAdj);
return msg;
}
@@ -3325,7 +3357,7 @@
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
- realStartServiceLocked(r, app, execInFg);
+ realStartServiceLocked(r, app, execInFg, enqueueOomAdj);
return null;
} catch (TransactionTooLargeException e) {
throw e;
@@ -3366,7 +3398,7 @@
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": process is bad";
Slog.w(TAG, msg);
- bringDownServiceLocked(r);
+ bringDownServiceLocked(r, enqueueOomAdj);
return msg;
}
if (isolated) {
@@ -3379,7 +3411,7 @@
Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid)
+ " for fg-service launch");
}
- mAm.tempWhitelistUidLocked(r.appInfo.uid,
+ mAm.tempAllowlistUidLocked(r.appInfo.uid,
SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch",
BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED);
}
@@ -3394,7 +3426,7 @@
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Applying delayed stop (in bring up): " + r);
- stopServiceLocked(r);
+ stopServiceLocked(r, enqueueOomAdj);
}
}
@@ -3417,7 +3449,7 @@
* from bindService() as well.
*/
private final void realStartServiceLocked(ServiceRecord r,
- ProcessRecord app, boolean execInFg) throws RemoteException {
+ ProcessRecord app, boolean execInFg, boolean enqueueOomAdj) throws RemoteException {
if (app.thread == null) {
throw new RemoteException();
}
@@ -3431,7 +3463,11 @@
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
- mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
+ if (enqueueOomAdj) {
+ mAm.enqueueOomAdjTargetLocked(app);
+ } else {
+ mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
+ }
boolean created = false;
try {
@@ -3466,7 +3502,7 @@
if (!created) {
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
- serviceDoneExecutingLocked(r, inDestroying, inDestroying);
+ serviceDoneExecutingLocked(r, inDestroying, inDestroying, false);
// Cleanup.
if (newService) {
@@ -3482,7 +3518,7 @@
}
if (r.whitelistManager) {
- app.whitelistManager = true;
+ app.mAllowlistManager = true;
}
requestServiceBindingsLocked(r, execInFg);
@@ -3515,7 +3551,7 @@
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Applying delayed stop (from start): " + r);
- stopServiceLocked(r);
+ stopServiceLocked(r, enqueueOomAdj);
}
}
}
@@ -3603,9 +3639,11 @@
if (caughtException != null) {
// Keep nesting count correct
final boolean inDestroying = mDestroyingServices.contains(r);
- for (int i = 0; i < args.size(); i++) {
- serviceDoneExecutingLocked(r, inDestroying, inDestroying);
+ for (int i = 0, size = args.size(); i < size; i++) {
+ serviceDoneExecutingLocked(r, inDestroying, inDestroying, true);
}
+ /* Will be a no-op if nothing pending */
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
if (caughtException instanceof TransactionTooLargeException) {
throw (TransactionTooLargeException)caughtException;
}
@@ -3631,7 +3669,7 @@
}
private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
- boolean hasConn) {
+ boolean hasConn, boolean enqueueOomAdj) {
//Slog.i(TAG, "Bring down service:");
//r.dump(" ");
@@ -3644,10 +3682,10 @@
return;
}
- bringDownServiceLocked(r);
+ bringDownServiceLocked(r, enqueueOomAdj);
}
- private final void bringDownServiceLocked(ServiceRecord r) {
+ private void bringDownServiceLocked(ServiceRecord r, boolean enqueueOomAdj) {
//Slog.i(TAG, "Bring down service:");
//r.dump(" ");
@@ -3672,9 +3710,9 @@
}
}
+ boolean needOomAdj = false;
// Tell the service that it has been unbound.
if (r.app != null && r.app.thread != null) {
- boolean needOomAdj = false;
for (int i = r.bindings.size() - 1; i >= 0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing down binding " + ibr
@@ -3691,15 +3729,11 @@
Slog.w(TAG, "Exception when unbinding service "
+ r.shortInstanceName, e);
needOomAdj = false;
- serviceProcessGoneLocked(r);
+ serviceProcessGoneLocked(r, enqueueOomAdj);
break;
}
}
}
- if (needOomAdj) {
- mAm.updateOomAdjLocked(r.app, true,
- OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
- }
}
// Check to see if the service had been started as foreground, but being
@@ -3801,7 +3835,7 @@
r.app.stopService(r);
r.app.updateBoundClientUids();
if (r.whitelistManager) {
- updateWhitelistManagerLocked(r.app);
+ updateAllowlistManagerLocked(r.app);
}
if (r.app.thread != null) {
updateServiceForegroundLocked(r.app, false);
@@ -3815,7 +3849,7 @@
} catch (Exception e) {
Slog.w(TAG, "Exception when destroying service "
+ r.shortInstanceName, e);
- serviceProcessGoneLocked(r);
+ serviceProcessGoneLocked(r, enqueueOomAdj);
}
} else {
if (DEBUG_SERVICE) Slog.v(
@@ -3826,6 +3860,13 @@
TAG_SERVICE, "Removed service that is not running: " + r);
}
+ if (needOomAdj) {
+ if (enqueueOomAdj) {
+ mAm.enqueueOomAdjTargetLocked(r.app);
+ } else {
+ mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ }
+ }
if (r.bindings.size() > 0) {
r.bindings.clear();
}
@@ -3849,7 +3890,7 @@
}
void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
- ActivityServiceConnectionsHolder skipAct) {
+ ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
@@ -3875,7 +3916,7 @@
if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
s.updateWhitelistManager();
if (!s.whitelistManager && s.app != null) {
- updateWhitelistManagerLocked(s.app);
+ updateAllowlistManagerLocked(s.app);
}
}
// And do the same for bg activity starts ability.
@@ -3918,8 +3959,12 @@
// it to go down there and we want it to start out near the top.
mAm.updateLruProcessLocked(s.app, false, null);
}
- mAm.updateOomAdjLocked(s.app, true,
- OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ if (enqueueOomAdj) {
+ mAm.enqueueOomAdjTargetLocked(s.app);
+ } else {
+ mAm.updateOomAdjLocked(s.app, true,
+ OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ }
b.intent.hasBound = false;
// Assume the client doesn't want to know about a rebind;
// we will deal with that later if it asks for one.
@@ -3927,7 +3972,7 @@
s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
} catch (Exception e) {
Slog.w(TAG, "Exception when unbinding service " + s.shortInstanceName, e);
- serviceProcessGoneLocked(s);
+ serviceProcessGoneLocked(s, enqueueOomAdj);
}
}
@@ -3946,12 +3991,13 @@
SystemClock.uptimeMillis());
}
}
- bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
+ bringDownServiceIfNeededLocked(s, true, hasAutoCreate, enqueueOomAdj);
}
}
}
- void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
+ void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,
+ boolean enqueueOomAdj) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
@@ -4024,7 +4070,7 @@
}
}
final long origId = Binder.clearCallingIdentity();
- serviceDoneExecutingLocked(r, inDestroying, inDestroying);
+ serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj);
Binder.restoreCallingIdentity(origId);
} else {
Slog.w(TAG, "Done executing unknown service from pid "
@@ -4032,7 +4078,7 @@
}
}
- private void serviceProcessGoneLocked(ServiceRecord r) {
+ private void serviceProcessGoneLocked(ServiceRecord r, boolean enqueueOomAdj) {
if (r.tracker != null) {
int memFactor = mAm.mProcessStats.getMemFactorLocked();
long now = SystemClock.uptimeMillis();
@@ -4041,11 +4087,11 @@
r.tracker.setBound(false, memFactor, now);
r.tracker.setStarted(false, memFactor, now);
}
- serviceDoneExecutingLocked(r, true, true);
+ serviceDoneExecutingLocked(r, true, true, enqueueOomAdj);
}
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
- boolean finishing) {
+ boolean finishing, boolean enqueueOomAdj) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "<<< DONE EXECUTING " + r
+ ": nesting=" + r.executeNesting
+ ", inDestroying=" + inDestroying + ", app=" + r.app);
@@ -4077,7 +4123,11 @@
mDestroyingServices.remove(r);
r.bindings.clear();
}
- mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ if (enqueueOomAdj) {
+ mAm.enqueueOomAdjTargetLocked(r.app);
+ } else {
+ mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ }
}
r.executeFg = false;
if (r.tracker != null) {
@@ -4095,7 +4145,7 @@
r.app.stopService(r);
r.app.updateBoundClientUids();
if (r.whitelistManager) {
- updateWhitelistManagerLocked(r.app);
+ updateAllowlistManagerLocked(r.app);
}
}
r.setProcess(null);
@@ -4121,15 +4171,17 @@
i--;
proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode,
mAm.mProcessStats);
- realStartServiceLocked(sr, proc, sr.createdFromFg);
+ realStartServiceLocked(sr, proc, sr.createdFromFg, true);
didSomething = true;
if (!isServiceNeededLocked(sr, false, false)) {
// We were waiting for this service to start, but it is actually no
// longer needed. This could happen because bringDownServiceIfNeeded
// won't bring down a service that is pending... so now the pending
// is done, so let's drop it.
- bringDownServiceLocked(sr);
+ bringDownServiceLocked(sr, true);
}
+ /* Will be a no-op if nothing pending */
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting service "
@@ -4157,7 +4209,8 @@
}
void processStartTimedOutLocked(ProcessRecord proc) {
- for (int i=0; i<mPendingServices.size(); i++) {
+ boolean needOomAdj = false;
+ for (int i = 0, size = mPendingServices.size(); i < size; i++) {
ServiceRecord sr = mPendingServices.get(i);
if ((proc.uid == sr.appInfo.uid
&& proc.processName.equals(sr.processName))
@@ -4165,10 +4218,15 @@
Slog.w(TAG, "Forcing bringing down service: " + sr);
sr.isolatedProc = null;
mPendingServices.remove(i);
+ size = mPendingServices.size();
i--;
- bringDownServiceLocked(sr);
+ needOomAdj = true;
+ bringDownServiceLocked(sr, true);
}
}
+ if (needOomAdj) {
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ }
}
private boolean collectPackageServicesLocked(String packageName, Set<String> filterByClasses,
@@ -4191,7 +4249,7 @@
service.app.stopService(service);
service.app.updateBoundClientUids();
if (service.whitelistManager) {
- updateWhitelistManagerLocked(service.app);
+ updateAllowlistManagerLocked(service.app);
}
}
service.setProcess(null);
@@ -4237,8 +4295,12 @@
}
if (mTmpCollectionResults != null) {
- for (int i = mTmpCollectionResults.size() - 1; i >= 0; i--) {
- bringDownServiceLocked(mTmpCollectionResults.get(i));
+ final int size = mTmpCollectionResults.size();
+ for (int i = size - 1; i >= 0; i--) {
+ bringDownServiceLocked(mTmpCollectionResults.get(i), true);
+ }
+ if (size > 0) {
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
}
mTmpCollectionResults.clear();
}
@@ -4279,12 +4341,14 @@
}
// Take care of any running services associated with the app.
+ boolean needOomAdj = false;
for (int i = services.size() - 1; i >= 0; i--) {
ServiceRecord sr = services.get(i);
if (sr.startRequested) {
if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
Slog.i(TAG, "Stopping service " + sr.shortInstanceName + ": remove task");
- stopServiceLocked(sr);
+ needOomAdj = true;
+ stopServiceLocked(sr, true);
} else {
sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
sr.getLastStartId(), baseIntent, null, 0));
@@ -4300,6 +4364,9 @@
}
}
}
+ if (needOomAdj) {
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ }
}
final void killServicesLocked(ProcessRecord app, boolean allowRestart) {
@@ -4333,12 +4400,12 @@
// Clean up any connections this application has to other services.
for (int i = app.connections.size() - 1; i >= 0; i--) {
ConnectionRecord r = app.connections.valueAt(i);
- removeConnectionLocked(r, app, null);
+ removeConnectionLocked(r, app, null, true);
}
updateServiceConnectionActivitiesLocked(app);
app.connections.clear();
- app.whitelistManager = false;
+ app.mAllowlistManager = false;
// Clear app state from services.
for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
@@ -4435,10 +4502,10 @@
+ " times, stopping: " + sr);
EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
sr.userId, sr.crashCount, sr.shortInstanceName, app.pid);
- bringDownServiceLocked(sr);
+ bringDownServiceLocked(sr, true);
} else if (!allowRestart
|| !mAm.mUserController.isUserRunning(sr.userId, 0)) {
- bringDownServiceLocked(sr);
+ bringDownServiceLocked(sr, true);
} else {
final boolean scheduled = scheduleServiceRestartLocked(sr, true /* allowCancel */);
@@ -4446,7 +4513,7 @@
// extreme case of so many attempts to deliver a command
// that it failed we also will stop it here.
if (!scheduled) {
- bringDownServiceLocked(sr);
+ bringDownServiceLocked(sr, true);
} else if (sr.canStopIfKilled(false /* isStartCanceled */)) {
// Update to stopped state because the explicit start is gone. The service is
// scheduled to restart for other reason (e.g. connections) so we don't bring
@@ -4460,6 +4527,8 @@
}
}
+ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+
if (!allowRestart) {
app.stopAllServices();
app.clearBoundClientUids();
@@ -4684,7 +4753,7 @@
Slog.i(TAG, "Service foreground-required timeout for " + r);
}
r.fgWaiting = false;
- stopServiceLocked(r);
+ stopServiceLocked(r, false);
}
if (app != null) {
@@ -5445,8 +5514,8 @@
for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
if (pr.uid == callingUid) {
- if (pr.mAllowStartFgs) {
- ret = FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD;
+ if (pr.mAllowStartFgs != FGS_FEATURE_DENIED) {
+ ret = pr.mAllowStartFgs;
break;
} else if (pr.isAllowedStartFgsState()) {
ret = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
@@ -5490,7 +5559,7 @@
}
if (ret == FGS_FEATURE_DENIED) {
- if (mAm.isWhitelistedForFgsStartLocked(callingUid)) {
+ if (mAm.isAllowlistedForFgsStartLocked(callingUid)) {
// uid is on DeviceIdleController's user/system allowlist
// or AMS's FgsStartTempAllowList.
ret = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
@@ -5566,7 +5635,7 @@
return CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, uid);
}
- private static String fgsCodeToString(@FgsFeatureRetCode int code) {
+ static String fgsCodeToString(@FgsFeatureRetCode int code) {
switch (code) {
case FGS_FEATURE_DENIED:
return "DENIED";
@@ -5593,7 +5662,7 @@
case FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION:
return "ALLOWED_BY_BACKGROUND_FGS_PERMISSION";
case FGS_FEATURE_ALLOWED_BY_ALLOWLIST:
- return "ALLOWED_BY_WHITELIST";
+ return "ALLOWED_BY_ALLOWLIST";
case FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER:
return "ALLOWED_BY_DEVICE_OWNER";
case FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST:
@@ -5652,4 +5721,23 @@
return mAm.mConstants.mFlagFgsStartRestrictionEnabled
&& CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
}
+
+ private void logFgsBackgroundStart(ServiceRecord r) {
+ // Only log if FGS is started from background.
+ if (!isFgsBgStart(r.mAllowStartForeground)) {
+ return;
+ }
+ if (!r.mLoggedInfoAllowStartForeground) {
+ final String msg = "Background started FGS: "
+ + ((r.mAllowStartForeground != FGS_FEATURE_DENIED) ? "Allowed " : "Disallowed ")
+ + r.mInfoAllowStartForeground;
+ Slog.wtfQuiet(TAG, msg);
+ if (r.mAllowStartForeground != FGS_FEATURE_DENIED) {
+ Slog.i(TAG, msg);
+ } else {
+ Slog.w(TAG, msg);
+ }
+ r.mLoggedInfoAllowStartForeground = true;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 05f2fa0..8a8d487 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -99,7 +99,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
@@ -237,6 +236,7 @@
import android.os.FactoryTest;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IDeviceIdentifiersPolicyService;
import android.os.IPermissionController;
@@ -249,9 +249,9 @@
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
+import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteCallback;
-import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -278,7 +278,6 @@
import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.DebugUtils;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Log;
@@ -319,6 +318,7 @@
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
+import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -329,7 +329,6 @@
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.server.AlarmManagerInternal;
-import com.android.server.AttributeCache;
import com.android.server.DeviceIdleInternal;
import com.android.server.DisplayThread;
import com.android.server.IntentResolver;
@@ -395,6 +394,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -535,16 +535,16 @@
public OomAdjProfiler mOomAdjProfiler = new OomAdjProfiler();
// Whether we should use SCHED_FIFO for UI and RenderThreads.
- boolean mUseFifoUiScheduling = false;
+ final boolean mUseFifoUiScheduling;
// Use an offload queue for long broadcasts, e.g. BOOT_COMPLETED.
// For simplicity, since we statically declare the size of the array of BroadcastQueues,
// we still create this new offload queue, but never ever put anything on it.
- boolean mEnableOffloadQueue;
+ final boolean mEnableOffloadQueue;
- BroadcastQueue mFgBroadcastQueue;
- BroadcastQueue mBgBroadcastQueue;
- BroadcastQueue mOffloadBroadcastQueue;
+ final BroadcastQueue mFgBroadcastQueue;
+ final BroadcastQueue mBgBroadcastQueue;
+ final BroadcastQueue mOffloadBroadcastQueue;
// Convenient for easy iteration over the queues. Foreground is first
// so that dispatch of foreground broadcasts gets precedence.
final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[3];
@@ -662,9 +662,9 @@
final ProcessStatsService mProcessStats;
/**
- * Non-persistent appId whitelist for background restrictions
+ * Non-persistent appId allowlist for background restrictions
*/
- int[] mBackgroundAppIdWhitelist = new int[] {
+ int[] mBackgroundAppIdAllowlist = new int[] {
BLUETOOTH_UID
};
@@ -881,11 +881,6 @@
*/
final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
- /**
- * List of processes that should gc as soon as things are idle.
- */
- final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
-
private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
new ActivityMetricsLaunchObserver() {
@Override
@@ -915,7 +910,7 @@
}
};
- private boolean mBinderTransactionTrackingEnabled = false;
+ private volatile boolean mBinderTransactionTrackingEnabled = false;
/**
* Fingerprints (hashCode()) of stack traces that we've
@@ -923,6 +918,7 @@
* something (rogue user app) forces this over
* MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared.
*/
+ @GuardedBy("mAlreadyLoggedViolatedStacks")
private final HashSet<Integer> mAlreadyLoggedViolatedStacks = new HashSet<Integer>();
private static final int MAX_DUP_SUPPRESSED_STACKS = 5000;
@@ -1062,7 +1058,7 @@
/**
* Information about component usage
*/
- UsageStatsManagerInternal mUsageStatsService;
+ volatile UsageStatsManagerInternal mUsageStatsService;
/**
* Access to DeviceIdleController service.
@@ -1072,29 +1068,29 @@
/**
* Power-save whitelisted app-ids (not including except-idle-whitelisted ones).
*/
- int[] mDeviceIdleWhitelist = new int[0];
+ int[] mDeviceIdleAllowlist = new int[0];
/**
* Power-save whitelisted app-ids (including except-idle-whitelisted ones).
*/
- int[] mDeviceIdleExceptIdleWhitelist = new int[0];
+ int[] mDeviceIdleExceptIdleAllowlist = new int[0];
/**
* Set of app ids that are temporarily allowed to escape bg check due to high-pri message
*/
- int[] mDeviceIdleTempWhitelist = new int[0];
+ int[] mDeviceIdleTempAllowlist = new int[0];
- static final class PendingTempWhitelist {
+ static final class PendingTempAllowlist {
final int targetUid;
final long duration;
final String tag;
final int type;
- PendingTempWhitelist(int _targetUid, long _duration, String _tag, int _type) {
- targetUid = _targetUid;
- duration = _duration;
- tag = _tag;
- type = _type;
+ PendingTempAllowlist(int targetUid, long duration, String tag, int type) {
+ this.targetUid = targetUid;
+ this.duration = duration;
+ this.tag = tag;
+ this.type = type;
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -1107,7 +1103,7 @@
}
}
- final PendingTempWhitelists mPendingTempWhitelist = new PendingTempWhitelists(this);
+ final PendingTempAllowlists mPendingTempAllowlist = new PendingTempAllowlists(this);
/**
* The temp-allowlist that is allowed to start FGS from background.
@@ -1162,7 +1158,7 @@
/**
* State of external calls telling us if the device is awake or asleep.
*/
- int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ AtomicInteger mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
/**
* The uptime of the last time we performed idle maintenance.
@@ -1172,10 +1168,18 @@
/**
* For reporting to battery stats the current top application.
*/
+ @GuardedBy("mCurResumedAppLock")
private String mCurResumedPackage = null;
+
+ @GuardedBy("mCurResumedAppLock")
private int mCurResumedUid = -1;
/**
+ * Dedicated lock for {@link #mCurResumedPackage} and {@link #mCurResumedUid}.
+ */
+ private final Object mCurResumedAppLock = new Object();
+
+ /**
* For reporting to battery stats the apps currently running foreground
* service. The ProcessMap is package/uid tuples; each of these contain
* an array of the currently foreground processes.
@@ -1213,16 +1217,17 @@
int foregroundServiceTypes;
}
- // TODO: Move below 4 members and code to ProcessList
- final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
- ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
-
- final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>();
- final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>();
-
+ @GuardedBy("mOomAdjObserverLock")
OomAdjObserver mCurOomAdjObserver;
+
+ @GuardedBy("mOomAdjObserverLock")
int mCurOomAdjUid;
+ /**
+ * Dedicated lock for {@link #mCurOomAdjObserver} and {@link #mCurOomAdjUid}.
+ */
+ final Object mOomAdjObserverLock = new Object();
+
interface OomAdjObserver {
void onOomAdjMessage(String msg);
}
@@ -1301,7 +1306,7 @@
static final int IDLE_UIDS_MSG = 58;
static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
- static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
+ static final int PUSH_TEMP_ALLOWLIST_UI_MSG = 68;
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
static final int KILL_APP_ZYGOTE_MSG = 71;
@@ -1312,13 +1317,11 @@
static final String SERVICE_RECORD_KEY = "servicerecord";
- long mLastMemUsageReportTime = 0;
-
/**
* Flag whether the current user is a "monkey", i.e. whether
* the UI is driven by a UI automation tool.
*/
- private boolean mUserIsMonkey;
+ private volatile boolean mUserIsMonkey;
@VisibleForTesting
public final ServiceThread mHandlerThread;
@@ -1351,7 +1354,7 @@
/**
* Used to notify activity lifecycle events.
*/
- @Nullable ContentCaptureManagerInternal mContentCaptureService;
+ @Nullable volatile ContentCaptureManagerInternal mContentCaptureService;
/*
* The default duration for the binder heavy hitter auto sampler
@@ -1438,7 +1441,7 @@
}
} break;
case DISPATCH_PROCESSES_CHANGED_UI_MSG: {
- dispatchProcessesChanged();
+ mProcessList.dispatchProcessesChanged();
break;
}
case DISPATCH_PROCESS_DIED_UI_MSG: {
@@ -1447,14 +1450,14 @@
}
final int pid = msg.arg1;
final int uid = msg.arg2;
- dispatchProcessDied(pid, uid);
+ mProcessList.dispatchProcessDied(pid, uid);
break;
}
case DISPATCH_OOM_ADJ_OBSERVER_MSG: {
dispatchOomAdjObserver((String) msg.obj);
} break;
- case PUSH_TEMP_WHITELIST_UI_MSG: {
- pushTempWhitelist();
+ case PUSH_TEMP_ALLOWLIST_UI_MSG: {
+ pushTempAllowlist();
} break;
}
}
@@ -1470,18 +1473,18 @@
switch (msg.what) {
case GC_BACKGROUND_PROCESSES_MSG: {
synchronized (ActivityManagerService.this) {
- performAppGcsIfAppropriateLocked();
+ mAppProfiler.performAppGcsIfAppropriateLocked();
}
} break;
case SERVICE_TIMEOUT_MSG: {
- mServices.serviceTimeout((ProcessRecord)msg.obj);
+ mServices.serviceTimeout((ProcessRecord) msg.obj);
} break;
case SERVICE_FOREGROUND_TIMEOUT_MSG: {
- mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
+ mServices.serviceForegroundTimeout((ServiceRecord) msg.obj);
} break;
case SERVICE_FOREGROUND_CRASH_MSG: {
- mServices.serviceForegroundCrash(
- (ProcessRecord) msg.obj, msg.getData().getCharSequence(SERVICE_RECORD_KEY));
+ mServices.serviceForegroundCrash((ProcessRecord) msg.obj,
+ msg.getData().getCharSequence(SERVICE_RECORD_KEY));
} break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
@@ -1491,7 +1494,8 @@
try {
r.thread.updateTimeZone();
} catch (RemoteException ex) {
- Slog.w(TAG, "Failed to update time zone for: " + r.info.processName);
+ Slog.w(TAG, "Failed to update time zone for: "
+ + r.info.processName);
}
}
}
@@ -1506,13 +1510,13 @@
mProcessList.setAllHttpProxy();
} break;
case PROC_START_TIMEOUT_MSG: {
- ProcessRecord app = (ProcessRecord)msg.obj;
+ ProcessRecord app = (ProcessRecord) msg.obj;
synchronized (ActivityManagerService.this) {
processStartTimedOutLocked(app);
}
} break;
case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
- ProcessRecord app = (ProcessRecord)msg.obj;
+ ProcessRecord app = (ProcessRecord) msg.obj;
synchronized (ActivityManagerService.this) {
mCpHelper.processContentProviderPublishTimedOutLocked(app);
}
@@ -1521,13 +1525,14 @@
synchronized (ActivityManagerService.this) {
final int appId = msg.arg1;
final int userId = msg.arg2;
- Bundle bundle = (Bundle)msg.obj;
+ Bundle bundle = (Bundle) msg.obj;
String pkg = bundle.getString("pkg");
String reason = bundle.getString("reason");
forceStopPackageLocked(pkg, appId, false, false, true, false,
false, userId, reason);
}
} break;
+
case KILL_APP_ZYGOTE_MSG: {
synchronized (ActivityManagerService.this) {
final AppZygote appZygote = (AppZygote) msg.obj;
@@ -1541,10 +1546,10 @@
sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
} break;
case REPORT_MEM_USAGE_MSG: {
- final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj;
+ final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>) msg.obj;
Thread thread = new Thread() {
@Override public void run() {
- reportMemUsage(memInfos);
+ mAppProfiler.reportMemUsage(memInfos);
}
};
thread.start();
@@ -1680,7 +1685,10 @@
}
}
- public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
+ /**
+ * @param usageStatsManager shouldn't be null
+ */
+ public void setUsageStatsManager(@NonNull UsageStatsManagerInternal usageStatsManager) {
mUsageStatsService = usageStatsManager;
mActivityTaskManager.setUsageStatsManager(usageStatsManager);
}
@@ -2034,6 +2042,9 @@
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mInternal = new LocalService();
mPendingStartActivityUids = new PendingStartActivityUids(mContext);
+ mUseFifoUiScheduling = false;
+ mEnableOffloadQueue = false;
+ mFgBroadcastQueue = mBgBroadcastQueue = mOffloadBroadcastQueue = null;
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2089,11 +2100,19 @@
mEnableOffloadQueue = SystemProperties.getBoolean(
"persist.device_config.activity_manager_native_boot.offload_queue_enabled", false);
- mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
+ // Decouple broadcast-related timing operations from other OS activity by
+ // using a dedicated thread. Sharing this thread between queues is safe
+ // because we know the nature of the activity on it and can't stall
+ // unexpectedly.
+ HandlerThread broadcastThread = new HandlerThread("broadcast");
+ broadcastThread.start();
+ Handler broadcastHandler = broadcastThread.getThreadHandler();
+
+ mFgBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
"foreground", foreConstants, false);
- mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mBgBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
"background", backConstants, true);
- mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mOffloadBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
"offload", offloadConstants, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
@@ -2128,9 +2147,7 @@
mPendingIntentController = new PendingIntentController(
mHandlerThread.getLooper(), mUserController, mConstants);
- if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0) {
- mUseFifoUiScheduling = true;
- }
+ mUseFifoUiScheduling = SystemProperties.getInt("sys.use_fifo_ui", 0) != 0;
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
@@ -2244,7 +2261,7 @@
ArrayMap<String, ArraySet<String>> allowedAssociations =
SystemConfig.getInstance().getAllowedAssociations();
mAllowedAssociations = new ArrayMap<>(allowedAssociations.size());
- PackageManagerInternal pm = getPackageManagerInternalLocked();
+ PackageManagerInternal pm = getPackageManagerInternal();
for (int i = 0; i < allowedAssociations.size(); i++) {
final String pkg = allowedAssociations.keyAt(i);
final ArraySet<String> asc = allowedAssociations.valueAt(i);
@@ -2323,8 +2340,8 @@
}
}
- void updateCpuStatsLocked() {
- mAppProfiler.updateCpuStatsLocked();
+ void updateCpuStats() {
+ mAppProfiler.updateCpuStats();
}
void updateCpuStatsNow() {
@@ -2457,9 +2474,7 @@
}
void notifyPackageUse(String packageName, int reason) {
- synchronized(this) {
- getPackageManagerInternalLocked().notifyPackageUse(packageName, reason);
- }
+ getPackageManagerInternal().notifyPackageUse(packageName, reason);
}
boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs,
@@ -2538,10 +2553,11 @@
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(), taskRoot);
}
- if (mContentCaptureService != null && (event == Event.ACTIVITY_PAUSED
+ final ContentCaptureManagerInternal contentCaptureService = mContentCaptureService;
+ if (contentCaptureService != null && (event == Event.ACTIVITY_PAUSED
|| event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED
|| event == Event.ACTIVITY_DESTROYED)) {
- mContentCaptureService.notifyActivityEvent(userId, activity, event);
+ contentCaptureService.notifyActivityEvent(userId, activity, event);
}
}
@@ -2616,8 +2632,8 @@
for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
final ProcessRecord proc = mProcessList.mLruProcesses.get(i);
if (procState > proc.setProcState) {
- if (proc.pkgList.containsKey(packageName) ||
- (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
+ if (proc.getPkgList().containsKey(packageName)
+ || (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
procState = proc.setProcState;
}
}
@@ -2640,7 +2656,7 @@
if (app.thread == null) {
throw new IllegalArgumentException("Process has no app thread");
}
- if (app.trimMemoryLevel >= level) {
+ if (app.mProfile.getTrimMemoryLevel() >= level) {
throw new IllegalArgumentException(
"Unable to set a higher trim level than current level");
}
@@ -2650,119 +2666,14 @@
+ "on a foreground process");
}
app.thread.scheduleTrimMemory(level);
- app.trimMemoryLevel = level;
+ app.mProfile.setTrimMemoryLevel(level);
return true;
}
}
- private void dispatchProcessesChanged() {
- int N;
- synchronized (this) {
- N = mPendingProcessChanges.size();
- if (mActiveProcessChanges.length < N) {
- mActiveProcessChanges = new ProcessChangeItem[N];
- }
- mPendingProcessChanges.toArray(mActiveProcessChanges);
- mPendingProcessChanges.clear();
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "*** Delivering " + N + " process changes");
- }
-
- int i = mProcessObservers.beginBroadcast();
- while (i > 0) {
- i--;
- final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
- if (observer != null) {
- try {
- for (int j=0; j<N; j++) {
- ProcessChangeItem item = mActiveProcessChanges[j];
- if ((item.changes&ProcessChangeItem.CHANGE_ACTIVITIES) != 0) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "ACTIVITIES CHANGED pid=" + item.pid + " uid="
- + item.uid + ": " + item.foregroundActivities);
- observer.onForegroundActivitiesChanged(item.pid, item.uid,
- item.foregroundActivities);
- }
- if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES) != 0) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid="
- + item.uid + ": " + item.foregroundServiceTypes);
- observer.onForegroundServicesChanged(item.pid, item.uid,
- item.foregroundServiceTypes);
- }
- }
- } catch (RemoteException e) {
- }
- }
- }
- mProcessObservers.finishBroadcast();
-
- synchronized (this) {
- for (int j=0; j<N; j++) {
- mAvailProcessChanges.add(mActiveProcessChanges[j]);
- }
- }
- }
-
- @GuardedBy("this")
- ProcessChangeItem enqueueProcessChangeItemLocked(int pid, int uid) {
- int i = mPendingProcessChanges.size()-1;
- ActivityManagerService.ProcessChangeItem item = null;
- while (i >= 0) {
- item = mPendingProcessChanges.get(i);
- if (item.pid == pid) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "Re-using existing item: " + item);
- break;
- }
- i--;
- }
-
- if (i < 0) {
- // No existing item in pending changes; need a new one.
- final int NA = mAvailProcessChanges.size();
- if (NA > 0) {
- item = mAvailProcessChanges.remove(NA-1);
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "Retrieving available item: " + item);
- } else {
- item = new ActivityManagerService.ProcessChangeItem();
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "Allocating new item: " + item);
- }
- item.changes = 0;
- item.pid = pid;
- item.uid = uid;
- if (mPendingProcessChanges.size() == 0) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "*** Enqueueing dispatch processes changed!");
- mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG)
- .sendToTarget();
- }
- mPendingProcessChanges.add(item);
- }
-
- return item;
- }
-
- private void dispatchProcessDied(int pid, int uid) {
- int i = mProcessObservers.beginBroadcast();
- while (i > 0) {
- i--;
- final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
- if (observer != null) {
- try {
- observer.onProcessDied(pid, uid);
- } catch (RemoteException e) {
- }
- }
- }
- mProcessObservers.finishBroadcast();
- }
-
void dispatchOomAdjObserver(String msg) {
OomAdjObserver observer;
- synchronized (this) {
+ synchronized (mOomAdjObserverLock) {
observer = mCurOomAdjObserver;
}
@@ -2772,32 +2683,26 @@
}
void setOomAdjObserver(int uid, OomAdjObserver observer) {
- synchronized (this) {
+ synchronized (mOomAdjObserverLock) {
mCurOomAdjUid = uid;
mCurOomAdjObserver = observer;
}
}
void clearOomAdjObserver() {
- synchronized (this) {
+ synchronized (mOomAdjObserverLock) {
mCurOomAdjUid = -1;
mCurOomAdjObserver = null;
}
}
- void reportOomAdjMessageLocked(String tag, String msg) {
- Slog.d(tag, msg);
- if (mCurOomAdjObserver != null) {
- mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget();
- }
- }
-
void reportUidInfoMessageLocked(String tag, String msg, int uid) {
Slog.i(TAG, msg);
- if (mCurOomAdjObserver != null && uid == mCurOomAdjUid) {
- mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget();
+ synchronized (mOomAdjObserverLock) {
+ if (mCurOomAdjObserver != null && uid == mCurOomAdjUid) {
+ mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget();
+ }
}
-
}
/**
@@ -2969,57 +2874,6 @@
return null;
}
- final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) {
- // If there are no longer any background processes running,
- // and the app that died was not running instrumentation,
- // then tell everyone we are now low on memory.
- if (!mProcessList.haveBackgroundProcessLocked()) {
- boolean doReport = Build.IS_DEBUGGABLE;
- if (doReport) {
- long now = SystemClock.uptimeMillis();
- if (now < (mLastMemUsageReportTime+5*60*1000)) {
- doReport = false;
- } else {
- mLastMemUsageReportTime = now;
- }
- }
- final ArrayList<ProcessMemInfo> memInfos
- = doReport ? new ArrayList<ProcessMemInfo>(mProcessList.getLruSizeLocked())
- : null;
- EventLogTags.writeAmLowMemory(mProcessList.getLruSizeLocked());
- long now = SystemClock.uptimeMillis();
- for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord rec = mProcessList.mLruProcesses.get(i);
- if (rec == dyingProc || rec.thread == null) {
- continue;
- }
- if (doReport) {
- memInfos.add(new ProcessMemInfo(rec.processName, rec.pid, rec.setAdj,
- rec.setProcState, rec.adjType, rec.makeAdjReason()));
- }
- if ((rec.lastLowMemory+mConstants.GC_MIN_INTERVAL) <= now) {
- // The low memory report is overriding any current
- // state for a GC request. Make sure to do
- // heavy/important/visible/foreground processes first.
- if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
- rec.lastRequestedGc = 0;
- } else {
- rec.lastRequestedGc = rec.lastLowMemory;
- }
- rec.reportLowMemory = true;
- rec.lastLowMemory = now;
- mProcessesToGc.remove(rec);
- addProcessToGcListLocked(rec);
- }
- }
- if (doReport) {
- Message msg = mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos);
- mHandler.sendMessage(msg);
- }
- scheduleAppGcsLocked();
- }
- }
-
@GuardedBy("this")
final void appDiedLocked(ProcessRecord app, String reason) {
appDiedLocked(app, app.pid, app.thread, false, reason);
@@ -3076,7 +2930,7 @@
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
}
if (doLowMem) {
- doLowMemReportIfNeededLocked(app);
+ mAppProfiler.doLowMemReportIfNeededLocked(app);
}
} else if (app.pid != pid) {
// A new process has already been started.
@@ -3379,55 +3233,55 @@
final long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
- synchronized(this) {
- // Instant packages are not protected
- if (getPackageManagerInternalLocked().isPackageDataProtected(
- resolvedUserId, packageName)) {
- throw new SecurityException(
- "Cannot clear data for a protected package: " + packageName);
- }
+ // Instant packages are not protected
+ if (getPackageManagerInternal().isPackageDataProtected(
+ resolvedUserId, packageName)) {
+ throw new SecurityException(
+ "Cannot clear data for a protected package: " + packageName);
+ }
- ApplicationInfo applicationInfo = null;
- try {
- applicationInfo = pm.getApplicationInfo(packageName,
- MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
- } catch (RemoteException e) {
- /* ignore */
- }
- appInfo = applicationInfo;
+ ApplicationInfo applicationInfo = null;
+ try {
+ applicationInfo = pm.getApplicationInfo(packageName,
+ MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ appInfo = applicationInfo;
- final boolean clearingOwnUidData = appInfo != null && appInfo.uid == uid;
+ final boolean clearingOwnUidData = appInfo != null && appInfo.uid == uid;
- if (!clearingOwnUidData && checkComponentPermission(permission.CLEAR_APP_USER_DATA,
+ if (!clearingOwnUidData && checkComponentPermission(permission.CLEAR_APP_USER_DATA,
pid, uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("PID " + pid + " does not have permission "
- + android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data"
- + " of package " + packageName);
- }
+ throw new SecurityException("PID " + pid + " does not have permission "
+ + android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data"
+ + " of package " + packageName);
+ }
- final boolean hasInstantMetadata = getPackageManagerInternalLocked()
- .hasInstantApplicationMetadata(packageName, resolvedUserId);
- final boolean isUninstalledAppWithoutInstantMetadata =
- (appInfo == null && !hasInstantMetadata);
- isInstantApp = (appInfo != null && appInfo.isInstantApp())
- || hasInstantMetadata;
- final boolean canAccessInstantApps = checkComponentPermission(
- permission.ACCESS_INSTANT_APPS, pid, uid, -1, true)
- == PackageManager.PERMISSION_GRANTED;
+ final boolean hasInstantMetadata = getPackageManagerInternal()
+ .hasInstantApplicationMetadata(packageName, resolvedUserId);
+ final boolean isUninstalledAppWithoutInstantMetadata =
+ (appInfo == null && !hasInstantMetadata);
+ isInstantApp = (appInfo != null && appInfo.isInstantApp())
+ || hasInstantMetadata;
+ final boolean canAccessInstantApps = checkComponentPermission(
+ permission.ACCESS_INSTANT_APPS, pid, uid, -1, true)
+ == PackageManager.PERMISSION_GRANTED;
- if (isUninstalledAppWithoutInstantMetadata || (isInstantApp
+ if (isUninstalledAppWithoutInstantMetadata || (isInstantApp
&& !canAccessInstantApps)) {
- Slog.w(TAG, "Invalid packageName: " + packageName);
- if (observer != null) {
- try {
- observer.onRemoveCompleted(packageName, false);
- } catch (RemoteException e) {
- Slog.i(TAG, "Observer no longer exists.");
- }
+ Slog.w(TAG, "Invalid packageName: " + packageName);
+ if (observer != null) {
+ try {
+ observer.onRemoveCompleted(packageName, false);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
}
- return false;
}
+ return false;
+ }
+ synchronized (this) {
if (appInfo != null) {
forceStopPackageLocked(packageName, appInfo.uid, "clear data");
mAtmInternal.removeRecentTasksByPackageName(packageName, resolvedUserId);
@@ -3566,7 +3420,7 @@
ApplicationExitInfo.SUBREASON_UNKNOWN,
"kill all background");
- doLowMemReportIfNeededLocked(null);
+ mAppProfiler.doLowMemReportIfNeededLocked(null);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -3623,7 +3477,7 @@
int[] users = userId == UserHandle.USER_ALL
? mUserController.getUsers() : new int[] { userId };
for (int user : users) {
- if (getPackageManagerInternalLocked().isPackageStateProtected(
+ if (getPackageManagerInternal().isPackageStateProtected(
packageName, user)) {
Slog.w(TAG, "Ignoring request to force stop protected package "
+ packageName + " u" + user);
@@ -3660,17 +3514,17 @@
@Override
public void addPackageDependency(String packageName) {
- synchronized (this) {
- int callingPid = Binder.getCallingPid();
- if (callingPid == myPid()) {
- // Yeah, um, no.
- return;
- }
- ProcessRecord proc;
- synchronized (mPidsSelfLocked) {
- proc = mPidsSelfLocked.get(Binder.getCallingPid());
- }
- if (proc != null) {
+ int callingPid = Binder.getCallingPid();
+ if (callingPid == myPid()) {
+ // Yeah, um, no.
+ return;
+ }
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(Binder.getCallingPid());
+ }
+ if (proc != null) {
+ synchronized (this) {
if (proc.pkgDeps == null) {
proc.pkgDeps = new ArraySet<String>(1);
}
@@ -3743,15 +3597,22 @@
}
}
- Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
+ final Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
for (int i=pids.length-1; i>=0; i--) {
- infos[i] = new Debug.MemoryInfo();
+ final Debug.MemoryInfo mi = infos[i] = new Debug.MemoryInfo();
final ProcessRecord proc;
final int oomAdj;
- synchronized (this) {
+ final ProcessProfileRecord profile;
+ synchronized (mAppProfiler.mProfilerLock) {
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pids[i]);
- oomAdj = proc != null ? proc.setAdj : 0;
+ if (proc != null) {
+ profile = proc.mProfile;
+ oomAdj = profile.getSetAdj();
+ } else {
+ profile = null;
+ oomAdj = 0;
+ }
}
}
final int targetUid = (proc != null) ? proc.uid : -1;
@@ -3766,43 +3627,43 @@
continue; // Not allowed to see other users.
}
}
- if (proc != null && proc.lastMemInfoTime >= lastNow && proc.lastMemInfo != null
- && !isCallerInstrumentedFromShell) {
- // It hasn't been long enough that we want to take another sample; return
- // the last one.
- infos[i].set(proc.lastMemInfo);
- continue;
+ if (proc != null) {
+ synchronized (mAppProfiler.mProfilerLock) {
+ if (profile.getLastMemInfoTime() >= lastNow && profile.getLastMemInfo() != null
+ && !isCallerInstrumentedFromShell) {
+ // It hasn't been long enough that we want to take another sample; return
+ // the last one.
+ mi.set(profile.getLastMemInfo());
+ continue;
+ }
+ }
}
final long startTime = SystemClock.currentThreadTimeMillis();
final Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(pids[i], memInfo);
- final long endTime = SystemClock.currentThreadTimeMillis();
- infos[i].set(memInfo);
+ final long duration = SystemClock.currentThreadTimeMillis() - startTime;
+ mi.set(memInfo);
if (proc != null) {
- synchronized (this) {
- proc.lastMemInfo = memInfo;
- proc.lastMemInfoTime = SystemClock.uptimeMillis();
- if (proc.thread != null && proc.setAdj == oomAdj) {
+ synchronized (mAppProfiler.mProfilerLock) {
+ profile.setLastMemInfo(memInfo);
+ profile.setLastMemInfoTime(SystemClock.uptimeMillis());
+ if (profile.getThread() != null && profile.getSetAdj() == oomAdj) {
// Record this for posterity if the process has been stable.
- synchronized (mProcessStats.mLock) {
- proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
- infos[i].getTotalUss(), infos[i].getTotalRss(), false,
- ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime - startTime,
- proc.pkgList.mPkgList);
- }
- for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ profile.addPss(mi.getTotalPss(),
+ mi.getTotalUss(), mi.getTotalRss(), false,
+ ProcessStats.ADD_PSS_EXTERNAL_SLOW, duration);
+ proc.getPkgList().forEachPackageProcessStats(holder -> {
FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
proc.info.uid,
holder.state.getName(),
holder.state.getPackage(),
- infos[i].getTotalPss(),
- infos[i].getTotalUss(),
- infos[i].getTotalRss(),
+ mi.getTotalPss(),
+ mi.getTotalUss(),
+ mi.getTotalRss(),
ProcessStats.ADD_PSS_EXTERNAL_SLOW,
- endTime-startTime,
+ duration,
holder.appVersion);
- }
+ });
}
}
}
@@ -3823,7 +3684,7 @@
final boolean allUids = mAtmInternal.isGetTasksAllowed(
"getProcessPss", callingPid, callingUid);
- long[] pss = new long[pids.length];
+ final long[] pss = new long[pids.length];
for (int i=pids.length-1; i>=0; i--) {
ProcessRecord proc;
int oomAdj;
@@ -3838,29 +3699,29 @@
// just leave it empty.
continue;
}
- long[] tmpUss = new long[3];
- long startTime = SystemClock.currentThreadTimeMillis();
- pss[i] = Debug.getPss(pids[i], tmpUss, null);
- long endTime = SystemClock.currentThreadTimeMillis();
+ final long[] tmpUss = new long[3];
+ final long startTime = SystemClock.currentThreadTimeMillis();
+ final long pi = pss[i] = Debug.getPss(pids[i], tmpUss, null);
+ final long duration = SystemClock.currentThreadTimeMillis() - startTime;
if (proc != null) {
- synchronized (this) {
- if (proc.thread != null && proc.setAdj == oomAdj) {
+ final ProcessProfileRecord profile = proc.mProfile;
+ synchronized (mAppProfiler.mProfilerLock) {
+ if (profile.getThread() != null && profile.getSetAdj() == oomAdj) {
// Record this for posterity if the process has been stable.
- synchronized (mProcessStats.mLock) {
- proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false,
- ProcessStats.ADD_PSS_EXTERNAL, endTime - startTime,
- proc.pkgList.mPkgList);
- }
- for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ profile.addPss(pi, tmpUss[0], tmpUss[2], false,
+ ProcessStats.ADD_PSS_EXTERNAL, duration);
+ proc.getPkgList().forEachPackageProcessStats(holder -> {
FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
proc.info.uid,
holder.state.getName(),
holder.state.getPackage(),
- pss[i], tmpUss[0], tmpUss[2],
- ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime,
+ pi,
+ tmpUss[0],
+ tmpUss[2],
+ ProcessStats.ADD_PSS_EXTERNAL,
+ duration,
holder.appVersion);
- }
+ });
}
}
}
@@ -4051,7 +3912,7 @@
Slog.i(TAG, "Force stopping u" + userId + ": " + reason);
}
- mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
+ mAppErrors.resetProcessCrashTime(packageName == null, appId, userId);
}
// Notify first that the package is stopped, so its process won't be restarted unexpectedly
@@ -4425,7 +4286,11 @@
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
mProcessList.updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
- app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
+ final long now = SystemClock.uptimeMillis();
+ synchronized (mAppProfiler.mProfilerLock) {
+ app.mProfile.setLastRequestedGc(now);
+ app.mProfile.setLastLowMemory(now);
+ }
} catch (Exception e) {
// We need kill the process group here. (b/148588589)
Slog.wtf(TAG, "Exception thrown during bind of " + app, e);
@@ -4645,7 +4510,6 @@
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
synchronized (ActivityManagerService.this) {
- mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
mAppProfiler.requestPssAllProcsLocked(
SystemClock.uptimeMillis(), true, false);
}
@@ -5439,7 +5303,7 @@
// If force-background-check is enabled, restrict all apps that aren't whitelisted.
if (mForceBackgroundCheck &&
!UserHandle.isCore(uid) &&
- !isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ true)) {
+ !isOnDeviceIdleAllowlistLocked(uid, /*allowExceptIdleToo=*/ true)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Force background check: " +
uid + "/" + packageName + " restricted");
@@ -5468,7 +5332,7 @@
}
// Non-persistent but background whitelisted?
- if (uidOnBackgroundWhitelist(uid)) {
+ if (uidOnBackgroundAllowlist(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on background whitelist; not restricted in background");
@@ -5477,7 +5341,7 @@
}
// Is this app on the battery whitelist?
- if (isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ false)) {
+ if (isOnDeviceIdleAllowlistLocked(uid, /*allowExceptIdleToo=*/ false)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on idle whitelist; not restricted in background");
@@ -5501,7 +5365,7 @@
if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) {
boolean ephemeral;
if (uidRec == null) {
- ephemeral = getPackageManagerInternalLocked().isPackageEphemeral(
+ ephemeral = getPackageManagerInternal().isPackageEphemeral(
UserHandle.getUserId(uid), packageName);
} else {
ephemeral = uidRec.ephemeral;
@@ -5525,8 +5389,8 @@
if (DEBUG_BACKGROUND_CHECK) {
Slog.d(TAG, "checkAllowBackground: uid=" + uid
+ " pkg=" + packageName + " startMode=" + startMode
- + " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid, false)
- + " onwhitelist(ei)=" + isOnDeviceIdleWhitelistLocked(uid, true));
+ + " onallowlist=" + isOnDeviceIdleAllowlistLocked(uid, false)
+ + " onallowlist(ei)=" + isOnDeviceIdleAllowlistLocked(uid, true));
}
if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
// This is an old app that has been forced into a "compatible as possible"
@@ -5552,38 +5416,38 @@
}
/**
- * @return whether a UID is in the system, user or temp doze whitelist.
+ * @return whether a UID is in the system, user or temp doze allowlist.
*/
- boolean isOnDeviceIdleWhitelistLocked(int uid, boolean allowExceptIdleToo) {
+ boolean isOnDeviceIdleAllowlistLocked(int uid, boolean allowExceptIdleToo) {
final int appId = UserHandle.getAppId(uid);
- final int[] whitelist = allowExceptIdleToo
- ? mDeviceIdleExceptIdleWhitelist
- : mDeviceIdleWhitelist;
+ final int[] allowlist = allowExceptIdleToo
+ ? mDeviceIdleExceptIdleAllowlist
+ : mDeviceIdleAllowlist;
- return Arrays.binarySearch(whitelist, appId) >= 0
- || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0
- || mPendingTempWhitelist.indexOfKey(uid) >= 0;
+ return Arrays.binarySearch(allowlist, appId) >= 0
+ || Arrays.binarySearch(mDeviceIdleTempAllowlist, appId) >= 0
+ || mPendingTempAllowlist.indexOfKey(uid) >= 0;
}
- boolean isWhitelistedForFgsStartLocked(int uid) {
- return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, UserHandle.getAppId(uid)) >= 0
+ boolean isAllowlistedForFgsStartLocked(int uid) {
+ return Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0
|| mFgsStartTempAllowList.isAllowed(uid);
}
/**
- * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
- * the whitelist
+ * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on
+ * the allowlist
*/
- String getPendingTempWhitelistTagForUidLocked(int uid) {
- final PendingTempWhitelist ptw = mPendingTempWhitelist.get(uid);
+ String getPendingTempAllowlistTagForUidLocked(int uid) {
+ final PendingTempAllowlist ptw = mPendingTempAllowlist.get(uid);
return ptw != null ? ptw.tag : null;
}
@VisibleForTesting
public void grantImplicitAccess(int userId, Intent intent, int visibleUid, int recipientAppId) {
- getPackageManagerInternalLocked().
- grantImplicitAccess(userId, intent, recipientAppId, visibleUid, true /*direct*/);
+ getPackageManagerInternal()
+ .grantImplicitAccess(userId, intent, recipientAppId, visibleUid, true /*direct*/);
}
/**
@@ -5831,14 +5695,18 @@
}
@VisibleForTesting
- public PackageManagerInternal getPackageManagerInternalLocked() {
+ public PackageManagerInternal getPackageManagerInternal() {
+ // Intentionally hold no locks: in case of race conditions, the mPackageManagerInt will
+ // be set to the same value anyway.
if (mPackageManagerInt == null) {
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
}
return mPackageManagerInt;
}
- private PermissionManagerServiceInternal getPermissionManagerInternalLocked() {
+ private PermissionManagerServiceInternal getPermissionManagerInternal() {
+ // Intentionally hold no locks: in case of race conditions, the mPermissionManagerInt will
+ // be set to the same value anyway.
if (mPermissionManagerInt == null) {
mPermissionManagerInt =
LocalServices.getService(PermissionManagerServiceInternal.class);
@@ -5983,12 +5851,11 @@
// GLOBAL MANAGEMENT
// =========================================================
- private boolean uidOnBackgroundWhitelist(final int uid) {
+ private boolean uidOnBackgroundAllowlist(final int uid) {
final int appId = UserHandle.getAppId(uid);
- final int[] whitelist = mBackgroundAppIdWhitelist;
- final int N = whitelist.length;
- for (int i = 0; i < N; i++) {
- if (appId == whitelist[i]) {
+ final int[] allowlist = mBackgroundAppIdAllowlist;
+ for (int i = 0, len = allowlist.length; i < len; i++) {
+ if (appId == allowlist[i]) {
return true;
}
}
@@ -6028,11 +5895,11 @@
Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist");
}
synchronized (this) {
- final int N = mBackgroundAppIdWhitelist.length;
- int[] newList = new int[N+1];
- System.arraycopy(mBackgroundAppIdWhitelist, 0, newList, 0, N);
- newList[N] = UserHandle.getAppId(uid);
- mBackgroundAppIdWhitelist = newList;
+ final int num = mBackgroundAppIdAllowlist.length;
+ int[] newList = new int[num + 1];
+ System.arraycopy(mBackgroundAppIdAllowlist, 0, newList, 0, num);
+ newList[num] = UserHandle.getAppId(uid);
+ mBackgroundAppIdAllowlist = newList;
}
}
@@ -6043,18 +5910,10 @@
abiOverride, zygotePolicyFlags);
}
- @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) {
+ boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -6089,7 +5948,7 @@
mPersistentStartingProcesses.add(app);
mProcessList.startProcessLocked(app, new HostingRecord("added application",
customProcess != null ? customProcess : app.processName),
- zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, abiOverride);
+ zygotePolicyFlags, disableHiddenApiChecks, abiOverride);
}
return app;
@@ -6150,16 +6009,16 @@
}
void reportCurWakefulnessUsageEvent() {
- reportGlobalUsageEvent(mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE
+ reportGlobalUsageEvent(mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE
? UsageEvents.Event.SCREEN_INTERACTIVE
: UsageEvents.Event.SCREEN_NON_INTERACTIVE);
}
void onWakefulnessChanged(int wakefulness) {
synchronized(this) {
- boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
+ boolean wasAwake = mWakefulness.getAndSet(wakefulness)
+ == PowerManagerInternal.WAKEFULNESS_AWAKE;
boolean isAwake = wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
- mWakefulness = wakefulness;
if (wasAwake != isAwake) {
// Also update state in a special way for running foreground services UI.
@@ -6269,17 +6128,16 @@
*/
@Override
public void setAgentApp(@NonNull String packageName, @Nullable String agent) {
- synchronized (this) {
- // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
- // its own permission.
- if (checkCallingPermission(
- android.Manifest.permission.SET_ACTIVITY_WATCHER) !=
- PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
- }
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission.
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
- mAppProfiler.setAgentAppLocked(packageName, agent);
+ synchronized (mAppProfiler.mProfilerLock) {
+ mAppProfiler.setAgentAppLPf(packageName, agent);
}
}
@@ -6296,7 +6154,7 @@
}
void setProfileApp(ApplicationInfo app, String processName, ProfilerInfo profilerInfo) {
- synchronized (this) {
+ synchronized (mAppProfiler.mProfilerLock) {
if (!Build.IS_DEBUGGABLE) {
boolean isAppDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
boolean isAppProfileable = app.isProfileableByShell();
@@ -6305,7 +6163,7 @@
+ "and not profileable by shell: " + app.packageName);
}
}
- mAppProfiler.setProfileAppLocked(processName, profilerInfo);
+ mAppProfiler.setProfileAppLPf(processName, profilerInfo);
}
}
@@ -6391,8 +6249,8 @@
Slog.w(TAG, "system process not in mPidsSelfLocked: " + myPid());
return;
}
- synchronized (this) {
- mAppProfiler.startHeapDumpLocked(pr, true);
+ synchronized (mAppProfiler.mProfilerLock) {
+ mAppProfiler.startHeapDumpLPf(pr.mProfile, true);
}
}
@@ -6572,16 +6430,12 @@
public void registerProcessObserver(IProcessObserver observer) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerProcessObserver()");
- synchronized (this) {
- mProcessObservers.register(observer);
- }
+ mProcessList.registerProcessObserver(observer);
}
@Override
public void unregisterProcessObserver(IProcessObserver observer) {
- synchronized (this) {
- mProcessObservers.unregister(observer);
- }
+ mProcessList.unregisterProcessObserver(observer);
}
@Override
@@ -6603,17 +6457,13 @@
enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
"registerUidObserver");
}
- synchronized (this) {
- mUidObserverController.register(observer, which, cutpoint, callingPackage,
- Binder.getCallingUid());
- }
+ mUidObserverController.register(observer, which, cutpoint, callingPackage,
+ Binder.getCallingUid());
}
@Override
public void unregisterUidObserver(IUidObserver observer) {
- synchronized (this) {
- mUidObserverController.unregister(observer);
- }
+ mUidObserverController.unregister(observer);
}
@Override
@@ -7205,28 +7055,35 @@
for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord proc = mProcessList.mLruProcesses.get(i);
+ final ProcessProfileRecord pr = proc.mProfile;
if (proc.notCachedSinceIdle) {
if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
- if (doKilling && proc.initialIdlePss != 0
- && proc.lastPss > ((proc.initialIdlePss * 3) / 2)
- && proc.lastPss > (proc.initialIdlePss + memoryGrowthThreshold)) {
+ final long initialIdlePss, lastPss, lastSwapPss;
+ synchronized (mAppProfiler.mProfilerLock) {
+ initialIdlePss = pr.getInitialIdlePss();
+ lastPss = pr.getLastPss();
+ lastSwapPss = pr.getLastSwapPss();
+ }
+ if (doKilling && initialIdlePss != 0
+ && lastPss > ((initialIdlePss * 3) / 2)
+ && lastPss > (initialIdlePss + memoryGrowthThreshold)) {
sb = new StringBuilder(128);
sb.append("Kill");
sb.append(proc.processName);
sb.append(" in idle maint: pss=");
- sb.append(proc.lastPss);
+ sb.append(lastPss);
sb.append(", swapPss=");
- sb.append(proc.lastSwapPss);
+ sb.append(lastSwapPss);
sb.append(", initialPss=");
- sb.append(proc.initialIdlePss);
+ sb.append(initialIdlePss);
sb.append(", period=");
TimeUtils.formatDuration(timeSinceLastIdle, sb);
sb.append(", lowRamPeriod=");
TimeUtils.formatDuration(lowRamSinceLastIdle, sb);
Slog.wtfQuiet(TAG, sb.toString());
- proc.kill("idle maint (pss " + proc.lastPss
- + " from " + proc.initialIdlePss + ")",
+ proc.kill("idle maint (pss " + lastPss
+ + " from " + initialIdlePss + ")",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_MEMORY_PRESSURE,
true);
@@ -7235,8 +7092,11 @@
} else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME
&& proc.setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) {
proc.notCachedSinceIdle = true;
- proc.initialIdlePss = 0;
- mAppProfiler.updateNextPssTimeLocked(proc.setProcState, proc, now, true);
+ synchronized (mAppProfiler.mProfilerLock) {
+ pr.setInitialIdlePss(0);
+ mAppProfiler.updateNextPssTimeLPf(
+ proc.setProcState, proc.mProfile, now, true);
+ }
}
}
}
@@ -7277,14 +7137,13 @@
mAppProfiler.retrieveSettings();
+ final Resources res;
synchronized (this) {
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
// Load resources only after the current configuration has been set.
- final Resources res = mContext.getResources();
- mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
- com.android.internal.R.string.config_appsNotReportingCrashes));
+ res = mContext.getResources();
final boolean userSwitchUiEnabled = !res.getBoolean(
com.android.internal.R.bool.config_customUserSwitchUi);
final int maxRunningUsers = res.getInteger(
@@ -7295,6 +7154,8 @@
delayUserDataLocking);
mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
}
+ mAppErrors.loadAppsNotReportingCrashesFromConfig(res.getString(
+ com.android.internal.R.string.config_appsNotReportingCrashes));
}
/**
@@ -7714,9 +7575,8 @@
if ((penaltyMask & StrictMode.PENALTY_DIALOG) != 0) {
AppErrorResult result = new AppErrorResult();
- synchronized (this) {
- final long origId = Binder.clearCallingIdentity();
-
+ final long origId = Binder.clearCallingIdentity();
+ try {
Message msg = Message.obtain();
msg.what = SHOW_STRICT_MODE_VIOLATION_UI_MSG;
HashMap<String, Object> data = new HashMap<String, Object>();
@@ -7725,7 +7585,7 @@
data.put("info", info);
msg.obj = data;
mUiHandler.sendMessage(msg);
-
+ } finally {
Binder.restoreCallingIdentity(origId);
}
int res = result.get();
@@ -7927,8 +7787,7 @@
int flags = process.info.flags;
IPackageManager pm = AppGlobals.getPackageManager();
sb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n");
- for (int ip=0; ip<process.pkgList.size(); ip++) {
- String pkg = process.pkgList.keyAt(ip);
+ process.getPkgList().forEachPackage(pkg -> {
sb.append("Package: ").append(pkg);
try {
PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId());
@@ -7942,7 +7801,7 @@
Slog.e(TAG, "Error getting package info: " + pkg, e);
}
sb.append("\n");
- }
+ });
if (process.info.isInstantApp()) {
sb.append("Instant-App: true\n");
}
@@ -8373,7 +8232,7 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+ dumpPermissions(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
sdumper = mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll, dumpPackage);
if (!dumpClient) {
@@ -8470,12 +8329,12 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
+ mProcessList.dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpUsersLocked(pw);
+ dumpUsers(pw);
}
}
@@ -8574,7 +8433,7 @@
}
// output proto is ProcessProto
synchronized (this) {
- writeProcessesToProtoLocked(proto, dumpPackage);
+ mProcessList.writeProcessesToProtoLocked(proto, dumpPackage);
}
} else {
// default option, dump everything, output is ActivityManagerServiceProto
@@ -8593,7 +8452,7 @@
proto.end(serviceToken);
long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
- writeProcessesToProtoLocked(proto, dumpPackage);
+ mProcessList.writeProcessesToProtoLocked(proto, dumpPackage);
proto.end(processToken);
}
}
@@ -8660,20 +8519,19 @@
dumpPackage = args[opti];
opti++;
}
- synchronized (this) {
- mPendingIntentController.dumpPendingIntents(pw, true, dumpPackage);
- }
+ mPendingIntentController.dumpPendingIntents(pw, true, dumpPackage);
} else if ("processes".equals(cmd) || "p".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
opti++;
}
synchronized (this) {
- dumpProcessesLocked(fd, pw, args, opti, true, dumpPackage, dumpAppId);
+ mProcessList.dumpProcessesLocked(
+ fd, pw, args, opti, true, dumpPackage, dumpAppId);
}
} else if ("oom".equals(cmd) || "o".equals(cmd)) {
synchronized (this) {
- dumpOomLocked(fd, pw, false, args, opti, true, dumpPackage, true);
+ mProcessList.dumpOomLocked(fd, pw, false, args, opti, true, dumpPackage, true);
}
} else if ("lmk".equals(cmd)) {
synchronized (this) {
@@ -8681,12 +8539,10 @@
}
} else if ("lru".equals(cmd)) {
synchronized (this) {
- dumpLruLocked(pw, dumpPackage, null);
+ mProcessList.dumpLruLocked(pw, dumpPackage, null);
}
} else if ("permissions".equals(cmd) || "perm".equals(cmd)) {
- synchronized (this) {
- dumpPermissionsLocked(fd, pw, args, opti, true, dumpPackage);
- }
+ dumpPermissions(fd, pw, args, opti, true, dumpPackage);
} else if ("provider".equals(cmd)) {
String[] newArgs;
String name;
@@ -8779,9 +8635,7 @@
} else if ("locks".equals(cmd)) {
LockGuard.dump(fd, pw, args);
} else if ("users".equals(cmd)) {
- synchronized (this) {
- dumpUsersLocked(pw);
- }
+ dumpUsers(pw);
} else if ("exit-info".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
@@ -8918,7 +8772,7 @@
}
}
- private int getAppId(String dumpPackage) {
+ int getAppId(String dumpPackage) {
if (dumpPackage != null) {
try {
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
@@ -8983,177 +8837,12 @@
" Counts of Binder Proxies held by SYSTEM");
}
- void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc, String prefix) {
- pw.print(prefix);
- pw.print('#');
- if (index < 10) {
- pw.print(' ');
- }
- pw.print(index);
- pw.print(": ");
- pw.print(ProcessList.makeOomAdjString(proc.setAdj, false));
- pw.print(' ');
- pw.print(ProcessList.makeProcStateString(proc.getCurProcState()));
- pw.print(' ');
- ActivityManager.printCapabilitiesSummary(pw, proc.curCapability);
- pw.print(' ');
- pw.print(proc.toShortString());
- if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities()
- || proc.treatLikeActivity) {
- pw.print(" act:");
- boolean printed = false;
- if (proc.hasActivities()) {
- pw.print("activities");
- printed = true;
- }
- if (proc.hasRecentTasks()) {
- if (printed) {
- pw.print("|");
- }
- pw.print("recents");
- printed = true;
- }
- if (proc.hasClientActivities()) {
- if (printed) {
- pw.print("|");
- }
- pw.print("client");
- printed = true;
- }
- if (proc.treatLikeActivity) {
- if (printed) {
- pw.print("|");
- }
- pw.print("treated");
- }
- }
- pw.println();
- }
-
- // TODO: Move to ProcessList?
- boolean dumpLruLocked(PrintWriter pw, String dumpPackage, String prefix) {
- final int N = mProcessList.mLruProcesses.size();
- final String innerPrefix;
- if (prefix == null) {
- pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)");
- innerPrefix = " ";
- } else {
- boolean haveAny = false;
- for (int i = N - 1; i >= 0; i--) {
- final ProcessRecord r = mProcessList.mLruProcesses.get(i);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
- continue;
- }
- haveAny = true;
- break;
- }
- if (!haveAny) {
- return false;
- }
- pw.print(prefix);
- pw.println("Raw LRU list (dumpsys activity lru):");
- innerPrefix = prefix + " ";
- }
- int i;
- boolean first = true;
- for (i = N - 1; i >= mProcessList.mLruProcessActivityStart; i--) {
- final ProcessRecord r = mProcessList.mLruProcesses.get(i);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
- continue;
- }
- if (first) {
- pw.print(innerPrefix);
- pw.println("Activities:");
- first = false;
- }
- dumpLruEntryLocked(pw, i, r, innerPrefix);
- }
- first = true;
- for (; i >= mProcessList.mLruProcessServiceStart; i--) {
- final ProcessRecord r = mProcessList.mLruProcesses.get(i);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
- continue;
- }
- if (first) {
- pw.print(innerPrefix);
- pw.println("Services:");
- first = false;
- }
- dumpLruEntryLocked(pw, i, r, innerPrefix);
- }
- first = true;
- for (; i >= 0; i--) {
- final ProcessRecord r = mProcessList.mLruProcesses.get(i);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
- continue;
- }
- if (first) {
- pw.print(innerPrefix);
- pw.println("Other:");
- first = false;
- }
- dumpLruEntryLocked(pw, i, r, innerPrefix);
- }
- return true;
- }
-
- // TODO: Move to ProcessList?
@GuardedBy("this")
- void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
- boolean needSep = false;
- int numPers = 0;
-
- pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
-
- if (dumpAll || dumpPackage != null) {
- final int NP = mProcessList.mProcessNames.getMap().size();
- for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
- final int NA = procs.size();
- for (int ia=0; ia<NA; ia++) {
- ProcessRecord r = procs.valueAt(ia);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
- continue;
- }
- if (!needSep) {
- pw.println(" All known processes:");
- needSep = true;
- }
- pw.print(r.isPersistent() ? " *PERS*" : " *APP*");
- pw.print(" UID "); pw.print(procs.keyAt(ia));
- pw.print(" "); pw.println(r);
- r.dump(pw, " ");
- if (r.isPersistent()) {
- numPers++;
- }
- }
- }
- }
-
- if (mProcessList.mIsolatedProcesses.size() > 0) {
+ boolean dumpActiveInstruments(PrintWriter pw, String dumpPackage, boolean needSep) {
+ final int size = mActiveInstrumentation.size();
+ if (size > 0) {
boolean printed = false;
- for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
- ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- pw.println(" Isolated process list (sorted by uid):");
- printed = true;
- needSep = true;
- }
- pw.print(" Isolated #"); pw.print(i); pw.print(": ");
- pw.println(r);
- }
- }
-
- if (mActiveInstrumentation.size() > 0) {
- boolean printed = false;
- for (int i=0; i<mActiveInstrumentation.size(); i++) {
+ for (int i = 0; i < size; i++) {
ActiveInstrumentation ai = mActiveInstrumentation.get(i);
if (dumpPackage != null && !ai.mClass.getPackageName().equals(dumpPackage)
&& !ai.mTargetInfo.packageName.equals(dumpPackage)) {
@@ -9172,48 +8861,20 @@
ai.dump(pw, " ");
}
}
+ return needSep;
+ }
- if (dumpOomLocked(fd, pw, needSep, args, opti, dumpAll, dumpPackage, false)) {
- needSep = true;
- }
-
- needSep = dumpProcessesToGc(pw, needSep, dumpPackage);
-
- if (mProcessList.mActiveUids.size() > 0) {
- needSep |= mProcessList.mActiveUids.dump(pw, dumpPackage, dumpAppId,
- "UID states:", needSep);
- }
-
- if (dumpAll) {
- needSep |= mUidObserverController.dumpValidateUids(pw,
- dumpPackage, dumpAppId, "UID validation:", needSep);
- }
-
- if (needSep) {
- pw.println();
- }
- if (dumpLruLocked(pw, dumpPackage, " ")) {
- needSep = true;
- }
-
- if (mProcessList.getLruSizeLocked() > 0) {
- if (needSep) {
- pw.println();
- }
- mProcessList.dumpLruListHeaderLocked(pw);
- dumpProcessOomList(pw, this, mProcessList.mLruProcesses, " ", "Proc", "PERS", false,
- dumpPackage);
- needSep = true;
- }
-
+ @GuardedBy("this")
+ void dumpOtherProcessesInfoLocked(FileDescriptor fd, PrintWriter pw,
+ boolean dumpAll, String dumpPackage, int dumpAppId, int numPers, boolean needSep) {
if (dumpAll || dumpPackage != null) {
final SparseArray<ProcessRecord> pidToProcess = new SparseArray<>();
synchronized (mPidsSelfLocked) {
boolean printed = false;
- for (int i=0; i<mPidsSelfLocked.size(); i++) {
+ for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
ProcessRecord r = mPidsSelfLocked.valueAt(i);
pidToProcess.put(r.pid, r);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
if (!printed) {
@@ -9229,10 +8890,11 @@
synchronized (sActiveProcessInfoSelfLocked) {
boolean printed = false;
- for (int i = 0; i < sActiveProcessInfoSelfLocked.size(); i++) {
+ for (int i = 0, size = sActiveProcessInfoSelfLocked.size(); i < size; i++) {
ProcessInfo info = sActiveProcessInfoSelfLocked.valueAt(i);
ProcessRecord r = pidToProcess.get(sActiveProcessInfoSelfLocked.keyAt(i));
- if (r != null && dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ if (r != null && dumpPackage != null
+ && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
if (!printed) {
@@ -9261,11 +8923,11 @@
if (mImportantProcesses.size() > 0) {
synchronized (mPidsSelfLocked) {
boolean printed = false;
- for (int i = 0; i< mImportantProcesses.size(); i++) {
+ for (int i = 0, size = mImportantProcesses.size(); i < size; i++) {
ProcessRecord r = mPidsSelfLocked.get(
mImportantProcesses.valueAt(i).pid);
if (dumpPackage != null && (r == null
- || !r.pkgList.containsKey(dumpPackage))) {
+ || !r.getPkgList().containsKey(dumpPackage))) {
continue;
}
if (!printed) {
@@ -9307,7 +8969,7 @@
needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep,
- mAppProfiler.getTestPssModeLocked(), mWakefulness);
+ mAppProfiler.getTestPssMode(), mWakefulness.get());
if (dumpAll && mProcessList.mPendingStarts.size() > 0) {
if (needSep) pw.println();
@@ -9321,14 +8983,14 @@
if (dumpAll) {
mUidObserverController.dump(pw, dumpPackage);
- pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist));
- pw.println(" mDeviceIdleExceptIdleWhitelist="
- + Arrays.toString(mDeviceIdleExceptIdleWhitelist));
- pw.println(" mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist));
- if (mPendingTempWhitelist.size() > 0) {
- pw.println(" mPendingTempWhitelist:");
- for (int i = 0; i < mPendingTempWhitelist.size(); i++) {
- PendingTempWhitelist ptw = mPendingTempWhitelist.valueAt(i);
+ pw.println(" mDeviceIdleAllowlist=" + Arrays.toString(mDeviceIdleAllowlist));
+ pw.println(" mDeviceIdleExceptIdleAllowlist="
+ + Arrays.toString(mDeviceIdleExceptIdleAllowlist));
+ pw.println(" mDeviceIdleTempAllowlist=" + Arrays.toString(mDeviceIdleTempAllowlist));
+ if (mPendingTempAllowlist.size() > 0) {
+ pw.println(" mPendingTempAllowlist:");
+ for (int i = 0, size = mPendingTempAllowlist.size(); i < size; i++) {
+ PendingTempAllowlist ptw = mPendingTempAllowlist.valueAt(i);
pw.print(" ");
UserHandle.formatUid(pw, ptw.targetUid);
pw.print(": ");
@@ -9353,7 +9015,9 @@
+ " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
}
}
- needSep = mAppProfiler.dumpMemWatchProcessesLocked(pw, needSep);
+ synchronized (mAppProfiler.mProfilerLock) {
+ needSep = mAppProfiler.dumpMemWatchProcessesLPf(pw, needSep);
+ }
if (mTrackAllocationApp != null) {
if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
if (needSep) {
@@ -9409,45 +9073,15 @@
pw.println(" mForceBackgroundCheck=" + mForceBackgroundCheck);
}
- @GuardedBy("this")
- private void dumpUsersLocked(PrintWriter pw) {
+ private void dumpUsers(PrintWriter pw) {
pw.println("ACTIVITY MANAGER USERS (dumpsys activity users)");
mUserController.dump(pw);
}
@GuardedBy("this")
- void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
- int numPers = 0;
-
- final int NP = mProcessList.mProcessNames.getMap().size();
- for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
- final int NA = procs.size();
- for (int ia = 0; ia<NA; ia++) {
- ProcessRecord r = procs.valueAt(ia);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
- continue;
- }
- r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.PROCS,
- mProcessList.mLruProcesses.indexOf(r)
- );
- if (r.isPersistent()) {
- numPers++;
- }
- }
- }
-
- for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
- ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
- continue;
- }
- r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS,
- mProcessList.mLruProcesses.indexOf(r)
- );
- }
-
- for (int i=0; i<mActiveInstrumentation.size(); i++) {
+ void writeOtherProcessesInfoToProtoLocked(ProtoOutputStream proto, String dumpPackage,
+ int dumpAppId, int numPers) {
+ for (int i = 0, size = mActiveInstrumentation.size(); i < size; i++) {
ActiveInstrumentation ai = mActiveInstrumentation.get(i);
if (dumpPackage != null && !ai.mClass.getPackageName().equals(dumpPackage)
&& !ai.mTargetInfo.packageName.equals(dumpPackage)) {
@@ -9457,32 +9091,14 @@
ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
}
- final int dumpAppId = getAppId(dumpPackage);
- mProcessList.mActiveUids.dumpProto(proto, dumpPackage, dumpAppId,
- ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
-
mUidObserverController.dumpValidateUidsProto(proto, dumpPackage, dumpAppId,
ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
- if (mProcessList.getLruSizeLocked() > 0) {
- long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
- int total = mProcessList.getLruSizeLocked();
- proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
- proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT,
- total - mProcessList.mLruProcessActivityStart);
- proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT,
- total - mProcessList.mLruProcessServiceStart);
- writeProcessOomListToProto(proto,
- ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
- mProcessList.mLruProcesses,false, dumpPackage);
- proto.end(lruToken);
- }
-
if (dumpPackage != null) {
synchronized (mPidsSelfLocked) {
for (int i=0; i<mPidsSelfLocked.size(); i++) {
ProcessRecord r = mPidsSelfLocked.valueAt(i);
- if (!r.pkgList.containsKey(dumpPackage)) {
+ if (!r.getPkgList().containsKey(dumpPackage)) {
continue;
}
r.dumpDebug(proto,
@@ -9493,11 +9109,11 @@
if (mImportantProcesses.size() > 0) {
synchronized (mPidsSelfLocked) {
- for (int i=0; i<mImportantProcesses.size(); i++) {
+ for (int i = 0, size = mImportantProcesses.size(); i < size; i++) {
ImportanceToken it = mImportantProcesses.valueAt(i);
ProcessRecord r = mPidsSelfLocked.get(it.pid);
if (dumpPackage != null && (r == null
- || !r.pkgList.containsKey(dumpPackage))) {
+ || !r.getPkgList().containsKey(dumpPackage))) {
continue;
}
it.dumpDebug(proto,
@@ -9506,7 +9122,7 @@
}
}
- for (int i=0; i<mPersistentStartingProcesses.size(); i++) {
+ for (int i = 0, size = mPersistentStartingProcesses.size(); i < size; i++) {
ProcessRecord r = mPersistentStartingProcesses.get(i);
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
@@ -9515,7 +9131,7 @@
ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
}
- for (int i = 0; i < mProcessList.mRemovedProcesses.size(); i++) {
+ for (int i = 0, size = mProcessList.mRemovedProcesses.size(); i < size; i++) {
ProcessRecord r = mProcessList.mRemovedProcesses.get(i);
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
@@ -9523,7 +9139,7 @@
r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.REMOVED_PROCS);
}
- for (int i=0; i<mProcessesOnHold.size(); i++) {
+ for (int i = 0, size = mProcessesOnHold.size(); i < size; i++) {
ProcessRecord r = mProcessesOnHold.get(i);
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
@@ -9531,12 +9147,15 @@
r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ON_HOLD_PROCS);
}
- writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS,
- dumpPackage);
+ synchronized (mAppProfiler.mProfilerLock) {
+ mAppProfiler.writeProcessesToGcToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.GC_PROCS,
+ dumpPackage);
+ }
mAppErrors.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS,
dumpPackage);
- mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness,
- mAppProfiler.getTestPssModeLocked());
+ mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness.get(),
+ mAppProfiler.getTestPssMode());
if (dumpPackage == null) {
mUserController.dumpDebug(proto,
@@ -9545,17 +9164,17 @@
mUidObserverController.dumpDebug(proto, dumpPackage);
- for (int v : mDeviceIdleWhitelist) {
+ for (int v : mDeviceIdleAllowlist) {
proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_WHITELIST, v);
}
- for (int v : mDeviceIdleTempWhitelist) {
+ for (int v : mDeviceIdleTempAllowlist) {
proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_TEMP_WHITELIST, v);
}
- if (mPendingTempWhitelist.size() > 0) {
- for (int i=0; i < mPendingTempWhitelist.size(); i++) {
- mPendingTempWhitelist.valueAt(i).dumpDebug(proto,
+ if (mPendingTempAllowlist.size() > 0) {
+ for (int i = 0, size = mPendingTempAllowlist.size(); i < size; i++) {
+ mPendingTempAllowlist.valueAt(i).dumpDebug(proto,
ActivityManagerServiceDumpProcessesProto.PENDING_TEMP_WHITELIST);
}
}
@@ -9573,7 +9192,9 @@
}
}
- mAppProfiler.writeMemWatchProcessToProtoLocked(proto);
+ synchronized (mAppProfiler.mProfilerLock) {
+ mAppProfiler.writeMemWatchProcessToProtoLPf(proto);
+ }
if (mTrackAllocationApp != null) {
if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
@@ -9608,113 +9229,6 @@
}
}
- void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
- if (mProcessesToGc.size() > 0) {
- long now = SystemClock.uptimeMillis();
- for (int i=0; i<mProcessesToGc.size(); i++) {
- ProcessRecord r = mProcessesToGc.get(i);
- if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
- continue;
- }
- final long token = proto.start(fieldId);
- r.dumpDebug(proto, ProcessToGcProto.PROC);
- proto.write(ProcessToGcProto.REPORT_LOW_MEMORY, r.reportLowMemory);
- proto.write(ProcessToGcProto.NOW_UPTIME_MS, now);
- proto.write(ProcessToGcProto.LAST_GCED_MS, r.lastRequestedGc);
- proto.write(ProcessToGcProto.LAST_LOW_MEMORY_MS, r.lastLowMemory);
- proto.end(token);
- }
- }
- }
-
- boolean dumpProcessesToGc(PrintWriter pw, boolean needSep, String dumpPackage) {
- if (mProcessesToGc.size() > 0) {
- boolean printed = false;
- long now = SystemClock.uptimeMillis();
- for (int i=0; i<mProcessesToGc.size(); i++) {
- ProcessRecord proc = mProcessesToGc.get(i);
- if (dumpPackage != null && !dumpPackage.equals(proc.info.packageName)) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println();
- needSep = true;
- pw.println(" Processes that are waiting to GC:");
- printed = true;
- }
- pw.print(" Process "); pw.println(proc);
- pw.print(" lowMem="); pw.print(proc.reportLowMemory);
- pw.print(", last gced=");
- pw.print(now-proc.lastRequestedGc);
- pw.print(" ms ago, last lowMem=");
- pw.print(now-proc.lastLowMemory);
- pw.println(" ms ago");
-
- }
- }
- return needSep;
- }
-
- void printOomLevel(PrintWriter pw, String name, int adj) {
- pw.print(" ");
- if (adj >= 0) {
- pw.print(' ');
- if (adj < 10) pw.print(' ');
- } else {
- if (adj > -10) pw.print(' ');
- }
- pw.print(adj);
- pw.print(": ");
- pw.print(name);
- pw.print(" (");
- pw.print(stringifySize(mProcessList.getMemLevel(adj), 1024));
- pw.println(")");
- }
-
- boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String[] args,
- int opti, boolean dumpAll, String dumpPackage, boolean inclGc) {
- if (mProcessList.getLruSizeLocked() > 0) {
- if (needSep) pw.println();
- needSep = true;
- pw.println(" OOM levels:");
- printOomLevel(pw, "SYSTEM_ADJ", ProcessList.SYSTEM_ADJ);
- printOomLevel(pw, "PERSISTENT_PROC_ADJ", ProcessList.PERSISTENT_PROC_ADJ);
- printOomLevel(pw, "PERSISTENT_SERVICE_ADJ", ProcessList.PERSISTENT_SERVICE_ADJ);
- printOomLevel(pw, "FOREGROUND_APP_ADJ", ProcessList.FOREGROUND_APP_ADJ);
- printOomLevel(pw, "VISIBLE_APP_ADJ", ProcessList.VISIBLE_APP_ADJ);
- printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", ProcessList.PERCEPTIBLE_APP_ADJ);
- printOomLevel(pw, "PERCEPTIBLE_LOW_APP_ADJ", ProcessList.PERCEPTIBLE_LOW_APP_ADJ);
- printOomLevel(pw, "BACKUP_APP_ADJ", ProcessList.BACKUP_APP_ADJ);
- printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", ProcessList.HEAVY_WEIGHT_APP_ADJ);
- printOomLevel(pw, "SERVICE_ADJ", ProcessList.SERVICE_ADJ);
- printOomLevel(pw, "HOME_APP_ADJ", ProcessList.HOME_APP_ADJ);
- printOomLevel(pw, "PREVIOUS_APP_ADJ", ProcessList.PREVIOUS_APP_ADJ);
- printOomLevel(pw, "SERVICE_B_ADJ", ProcessList.SERVICE_B_ADJ);
- printOomLevel(pw, "CACHED_APP_MIN_ADJ", ProcessList.CACHED_APP_MIN_ADJ);
- printOomLevel(pw, "CACHED_APP_MAX_ADJ", ProcessList.CACHED_APP_MAX_ADJ);
-
- if (needSep) pw.println();
- pw.print(" Process OOM control ("); pw.print(mProcessList.getLruSizeLocked());
- pw.print(" total, non-act at ");
- pw.print(mProcessList.getLruSizeLocked()
- - mProcessList.mLruProcessActivityStart);
- pw.print(", non-svc at ");
- pw.print(mProcessList.getLruSizeLocked()
- - mProcessList.mLruProcessServiceStart);
- pw.println("):");
- dumpProcessOomList(pw, this, mProcessList.mLruProcesses, " ", "Proc", "PERS", true,
- dumpPackage);
- needSep = true;
- }
-
- dumpProcessesToGc(pw, needSep, dumpPackage);
-
- pw.println();
- mAtmInternal.dumpForOom(pw);
-
- return true;
- }
-
private boolean reportLmkKillAtOrBelow(PrintWriter pw, int oom_adj) {
Integer cnt = ProcessList.getLmkdKillCount(0, oom_adj);
if (cnt != null) {
@@ -10050,8 +9564,7 @@
}
}
- @GuardedBy("this")
- void dumpPermissionsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ void dumpPermissions(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
pw.println("ACTIVITY MANAGER URI PERMISSIONS (dumpsys activity permissions)");
@@ -10080,270 +9593,6 @@
return numPers;
}
- private static final ArrayList<Pair<ProcessRecord, Integer>>
- sortProcessOomList(List<ProcessRecord> origList, String dumpPackage) {
- ArrayList<Pair<ProcessRecord, Integer>> list
- = new ArrayList<Pair<ProcessRecord, Integer>>(origList.size());
- for (int i=0; i<origList.size(); i++) {
- ProcessRecord r = origList.get(i);
- if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
- continue;
- }
- list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i));
- }
-
- Comparator<Pair<ProcessRecord, Integer>> comparator
- = new Comparator<Pair<ProcessRecord, Integer>>() {
- @Override
- public int compare(Pair<ProcessRecord, Integer> object1,
- Pair<ProcessRecord, Integer> object2) {
- if (object1.first.setAdj != object2.first.setAdj) {
- return object1.first.setAdj > object2.first.setAdj ? -1 : 1;
- }
- if (object1.first.setProcState != object2.first.setProcState) {
- return object1.first.setProcState > object2.first.setProcState ? -1 : 1;
- }
- if (object1.second.intValue() != object2.second.intValue()) {
- return object1.second.intValue() > object2.second.intValue() ? -1 : 1;
- }
- return 0;
- }
- };
-
- Collections.sort(list, comparator);
- return list;
- }
-
- private static final boolean writeProcessOomListToProto(ProtoOutputStream proto, long fieldId,
- ActivityManagerService service, List<ProcessRecord> origList,
- boolean inclDetails, String dumpPackage) {
- ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage);
- if (list.isEmpty()) return false;
-
- final long curUptime = SystemClock.uptimeMillis();
-
- for (int i = list.size() - 1; i >= 0; i--) {
- ProcessRecord r = list.get(i).first;
- long token = proto.start(fieldId);
- String oomAdj = ProcessList.makeOomAdjString(r.setAdj, true);
- proto.write(ProcessOomProto.PERSISTENT, r.isPersistent());
- proto.write(ProcessOomProto.NUM, (origList.size()-1)-list.get(i).second);
- proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
- int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
- switch (r.setSchedGroup) {
- case ProcessList.SCHED_GROUP_BACKGROUND:
- schedGroup = ProcessOomProto.SCHED_GROUP_BACKGROUND;
- break;
- case ProcessList.SCHED_GROUP_DEFAULT:
- schedGroup = ProcessOomProto.SCHED_GROUP_DEFAULT;
- break;
- case ProcessList.SCHED_GROUP_TOP_APP:
- schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP;
- break;
- case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
- schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP_BOUND;
- break;
- }
- if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) {
- proto.write(ProcessOomProto.SCHED_GROUP, schedGroup);
- }
- if (r.hasForegroundActivities()) {
- proto.write(ProcessOomProto.ACTIVITIES, true);
- } else if (r.hasForegroundServices()) {
- proto.write(ProcessOomProto.SERVICES, true);
- }
- proto.write(ProcessOomProto.STATE,
- ProcessList.makeProcStateProtoEnum(r.getCurProcState()));
- proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.trimMemoryLevel);
- r.dumpDebug(proto, ProcessOomProto.PROC);
- proto.write(ProcessOomProto.ADJ_TYPE, r.adjType);
- if (r.adjSource != null || r.adjTarget != null) {
- if (r.adjTarget instanceof ComponentName) {
- ComponentName cn = (ComponentName) r.adjTarget;
- cn.dumpDebug(proto, ProcessOomProto.ADJ_TARGET_COMPONENT_NAME);
- } else if (r.adjTarget != null) {
- proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, r.adjTarget.toString());
- }
- if (r.adjSource instanceof ProcessRecord) {
- ProcessRecord p = (ProcessRecord) r.adjSource;
- p.dumpDebug(proto, ProcessOomProto.ADJ_SOURCE_PROC);
- } else if (r.adjSource != null) {
- proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, r.adjSource.toString());
- }
- }
- if (inclDetails) {
- long detailToken = proto.start(ProcessOomProto.DETAIL);
- proto.write(ProcessOomProto.Detail.MAX_ADJ, r.maxAdj);
- proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.getCurRawAdj());
- proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, r.setRawAdj);
- proto.write(ProcessOomProto.Detail.CUR_ADJ, r.curAdj);
- proto.write(ProcessOomProto.Detail.SET_ADJ, r.setAdj);
- proto.write(ProcessOomProto.Detail.CURRENT_STATE,
- ProcessList.makeProcStateProtoEnum(r.getCurProcState()));
- proto.write(ProcessOomProto.Detail.SET_STATE,
- ProcessList.makeProcStateProtoEnum(r.setProcState));
- proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString(
- r.lastPss*1024, new StringBuilder()));
- proto.write(ProcessOomProto.Detail.LAST_SWAP_PSS, DebugUtils.sizeValueToString(
- r.lastSwapPss*1024, new StringBuilder()));
- proto.write(ProcessOomProto.Detail.LAST_CACHED_PSS, DebugUtils.sizeValueToString(
- r.lastCachedPss*1024, new StringBuilder()));
- proto.write(ProcessOomProto.Detail.CACHED, r.isCached());
- proto.write(ProcessOomProto.Detail.EMPTY, r.empty);
- proto.write(ProcessOomProto.Detail.HAS_ABOVE_CLIENT, r.hasAboveClient);
-
- if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
- if (r.lastCpuTime != 0) {
- long uptimeSince = curUptime - service.mLastPowerCheckUptime;
- long timeUsed = r.curCpuTime - r.lastCpuTime;
- long cpuTimeToken = proto.start(ProcessOomProto.Detail.SERVICE_RUN_TIME);
- proto.write(ProcessOomProto.Detail.CpuRunTime.OVER_MS, uptimeSince);
- proto.write(ProcessOomProto.Detail.CpuRunTime.USED_MS, timeUsed);
- proto.write(ProcessOomProto.Detail.CpuRunTime.ULTILIZATION,
- (100.0*timeUsed)/uptimeSince);
- proto.end(cpuTimeToken);
- }
- }
- proto.end(detailToken);
- }
- proto.end(token);
- }
-
- return true;
- }
-
- private static final boolean dumpProcessOomList(PrintWriter pw,
- ActivityManagerService service, List<ProcessRecord> origList,
- String prefix, String normalLabel, String persistentLabel,
- boolean inclDetails, String dumpPackage) {
-
- ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage);
- if (list.isEmpty()) return false;
-
- final long curUptime = SystemClock.uptimeMillis();
- final long uptimeSince = curUptime - service.mLastPowerCheckUptime;
-
- for (int i=list.size()-1; i>=0; i--) {
- ProcessRecord r = list.get(i).first;
- String oomAdj = ProcessList.makeOomAdjString(r.setAdj, false);
- char schedGroup;
- switch (r.setSchedGroup) {
- case ProcessList.SCHED_GROUP_BACKGROUND:
- schedGroup = 'b';
- break;
- case ProcessList.SCHED_GROUP_DEFAULT:
- schedGroup = 'F';
- break;
- case ProcessList.SCHED_GROUP_TOP_APP:
- schedGroup = 'T';
- break;
- case ProcessList.SCHED_GROUP_RESTRICTED:
- schedGroup = 'R';
- break;
- case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
- schedGroup = 'B';
- break;
- default:
- schedGroup = '?';
- break;
- }
- char foreground;
- if (r.hasForegroundActivities()) {
- foreground = 'A';
- } else if (r.hasForegroundServices()) {
- foreground = 'S';
- } else {
- foreground = ' ';
- }
- String procState = ProcessList.makeProcStateString(r.getCurProcState());
- pw.print(prefix);
- pw.print(r.isPersistent() ? persistentLabel : normalLabel);
- pw.print(" #");
- int num = (origList.size()-1)-list.get(i).second;
- if (num < 10) pw.print(' ');
- pw.print(num);
- pw.print(": ");
- pw.print(oomAdj);
- pw.print(' ');
- pw.print(schedGroup);
- pw.print('/');
- pw.print(foreground);
- pw.print('/');
- pw.print(procState);
- pw.print(' ');
- ActivityManager.printCapabilitiesSummary(pw, r.curCapability);
- pw.print(' ');
- pw.print(" t:");
- if (r.trimMemoryLevel < 10) pw.print(' ');
- pw.print(r.trimMemoryLevel);
- pw.print(' ');
- pw.print(r.toShortString());
- pw.print(" (");
- pw.print(r.adjType);
- pw.println(')');
- if (r.adjSource != null || r.adjTarget != null) {
- pw.print(prefix);
- pw.print(" ");
- if (r.adjTarget instanceof ComponentName) {
- pw.print(((ComponentName)r.adjTarget).flattenToShortString());
- } else if (r.adjTarget != null) {
- pw.print(r.adjTarget.toString());
- } else {
- pw.print("{null}");
- }
- pw.print("<=");
- if (r.adjSource instanceof ProcessRecord) {
- pw.print("Proc{");
- pw.print(((ProcessRecord)r.adjSource).toShortString());
- pw.println("}");
- } else if (r.adjSource != null) {
- pw.println(r.adjSource.toString());
- } else {
- pw.println("{null}");
- }
- }
- if (inclDetails) {
- pw.print(prefix);
- pw.print(" ");
- pw.print("oom: max="); pw.print(r.maxAdj);
- pw.print(" curRaw="); pw.print(r.getCurRawAdj());
- pw.print(" setRaw="); pw.print(r.setRawAdj);
- pw.print(" cur="); pw.print(r.curAdj);
- pw.print(" set="); pw.println(r.setAdj);
- pw.print(prefix);
- pw.print(" ");
- pw.print("state: cur="); pw.print(
- ProcessList.makeProcStateString(r.getCurProcState()));
- pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState));
- pw.print(" lastPss="); DebugUtils.printSizeValue(pw, r.lastPss*1024);
- pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, r.lastSwapPss*1024);
- pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, r.lastCachedPss*1024);
- pw.println();
- pw.print(prefix);
- pw.print(" ");
- pw.print("cached="); pw.print(r.isCached());
- pw.print(" empty="); pw.print(r.empty);
- pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
-
- if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
- if (r.lastCpuTime != 0) {
- long timeUsed = r.curCpuTime - r.lastCpuTime;
- pw.print(prefix);
- pw.print(" ");
- pw.print("run cpu over ");
- TimeUtils.formatDuration(uptimeSince, pw);
- pw.print(" used ");
- TimeUtils.formatDuration(timeUsed, pw);
- pw.print(" (");
- pw.print((timeUsed*100)/uptimeSince);
- pw.println("%)");
- }
- }
- }
- }
- return true;
- }
-
ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs,
String[] args) {
synchronized (this) {
@@ -10637,12 +9886,12 @@
}
}
- private static final int KSM_SHARED = 0;
- private static final int KSM_SHARING = 1;
- private static final int KSM_UNSHARED = 2;
- private static final int KSM_VOLATILE = 3;
+ static final int KSM_SHARED = 0;
+ static final int KSM_SHARING = 1;
+ static final int KSM_UNSHARED = 2;
+ static final int KSM_VOLATILE = 3;
- private final long[] getKsmInfo() {
+ static final long[] getKsmInfo() {
long[] longOut = new long[4];
final int[] SINGLE_LONG_FORMAT = new int[] {
PROC_SPACE_TERM| PROC_OUT_LONG
@@ -10666,7 +9915,7 @@
return longOut;
}
- private static String stringifySize(long size, int order) {
+ static String stringifySize(long size, int order) {
Locale locale = Locale.US;
switch (order) {
case 1:
@@ -10682,7 +9931,7 @@
}
}
- private static String stringifyKBSize(long size) {
+ static String stringifyKBSize(long size) {
return stringifySize(size * 1024, 1024);
}
@@ -10978,12 +10227,9 @@
synchronized (this) {
if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
// Record this for posterity if the process has been stable.
- synchronized (mProcessStats.mLock) {
- r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
- reportType, endTime - startTime, r.pkgList.mPkgList);
- }
- for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg);
+ r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
+ reportType, endTime - startTime);
+ r.getPkgList().forEachPackageProcessStats(holder -> {
FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
r.info.uid,
holder.state.getName(),
@@ -10991,7 +10237,7 @@
myTotalPss, myTotalUss, myTotalRss, reportType,
endTime-startTime,
holder.appVersion);
- }
+ });
}
}
@@ -11561,19 +10807,16 @@
synchronized (this) {
if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
// Record this for posterity if the process has been stable.
- synchronized (mProcessStats.mLock) {
- r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
- reportType, endTime - startTime, r.pkgList.mPkgList);
- }
- for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg);
+ r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
+ reportType, endTime - startTime);
+ r.getPkgList().forEachPackageProcessStats(holder -> {
FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
r.info.uid,
holder.state.getName(),
holder.state.getPackage(),
myTotalPss, myTotalUss, myTotalRss, reportType, endTime-startTime,
holder.appVersion);
- }
+ });
}
}
@@ -11844,7 +11087,7 @@
proto.flush();
}
- private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss,
+ static void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss,
long memtrack, String name) {
sb.append(" ");
sb.append(ProcessList.makeOomAdjString(oomAdj, false));
@@ -11861,7 +11104,7 @@
}
}
- private void appendMemInfo(StringBuilder sb, ProcessMemInfo mi) {
+ static void appendMemInfo(StringBuilder sb, ProcessMemInfo mi) {
appendBasicMemEntry(sb, mi.oomAdj, mi.procState, mi.pss, mi.memtrack, mi.name);
sb.append(" (pid ");
sb.append(mi.pid);
@@ -11875,283 +11118,6 @@
}
}
- void reportMemUsage(ArrayList<ProcessMemInfo> memInfos) {
- final SparseArray<ProcessMemInfo> infoMap = new SparseArray<>(memInfos.size());
- for (int i=0, N=memInfos.size(); i<N; i++) {
- ProcessMemInfo mi = memInfos.get(i);
- infoMap.put(mi.pid, mi);
- }
- updateCpuStatsNow();
- long[] memtrackTmp = new long[1];
- long[] swaptrackTmp = new long[2];
- // Get a list of Stats that have vsize > 0
- final List<ProcessCpuTracker.Stats> stats = mAppProfiler.getCpuStats(st -> st.vsize > 0);
- final int statsCount = stats.size();
- for (int i = 0; i < statsCount; i++) {
- ProcessCpuTracker.Stats st = stats.get(i);
- long pss = Debug.getPss(st.pid, swaptrackTmp, memtrackTmp);
- if (pss > 0) {
- if (infoMap.indexOfKey(st.pid) < 0) {
- ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid,
- ProcessList.NATIVE_ADJ, -1, "native", null);
- mi.pss = pss;
- mi.swapPss = swaptrackTmp[1];
- mi.memtrack = memtrackTmp[0];
- memInfos.add(mi);
- }
- }
- }
-
- long totalPss = 0;
- long totalSwapPss = 0;
- long totalMemtrack = 0;
- for (int i=0, N=memInfos.size(); i<N; i++) {
- ProcessMemInfo mi = memInfos.get(i);
- if (mi.pss == 0) {
- mi.pss = Debug.getPss(mi.pid, swaptrackTmp, memtrackTmp);
- mi.swapPss = swaptrackTmp[1];
- mi.memtrack = memtrackTmp[0];
- }
- totalPss += mi.pss;
- totalSwapPss += mi.swapPss;
- totalMemtrack += mi.memtrack;
- }
- Collections.sort(memInfos, new Comparator<ProcessMemInfo>() {
- @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) {
- if (lhs.oomAdj != rhs.oomAdj) {
- return lhs.oomAdj < rhs.oomAdj ? -1 : 1;
- }
- if (lhs.pss != rhs.pss) {
- return lhs.pss < rhs.pss ? 1 : -1;
- }
- return 0;
- }
- });
-
- StringBuilder tag = new StringBuilder(128);
- StringBuilder stack = new StringBuilder(128);
- tag.append("Low on memory -- ");
- appendMemBucket(tag, totalPss, "total", false);
- appendMemBucket(stack, totalPss, "total", true);
-
- StringBuilder fullNativeBuilder = new StringBuilder(1024);
- StringBuilder shortNativeBuilder = new StringBuilder(1024);
- StringBuilder fullJavaBuilder = new StringBuilder(1024);
-
- boolean firstLine = true;
- int lastOomAdj = Integer.MIN_VALUE;
- long extraNativeRam = 0;
- long extraNativeMemtrack = 0;
- long cachedPss = 0;
- for (int i=0, N=memInfos.size(); i<N; i++) {
- ProcessMemInfo mi = memInfos.get(i);
-
- if (mi.oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
- cachedPss += mi.pss;
- }
-
- if (mi.oomAdj != ProcessList.NATIVE_ADJ
- && (mi.oomAdj < ProcessList.SERVICE_ADJ
- || mi.oomAdj == ProcessList.HOME_APP_ADJ
- || mi.oomAdj == ProcessList.PREVIOUS_APP_ADJ)) {
- if (lastOomAdj != mi.oomAdj) {
- lastOomAdj = mi.oomAdj;
- if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) {
- tag.append(" / ");
- }
- if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ) {
- if (firstLine) {
- stack.append(":");
- firstLine = false;
- }
- stack.append("\n\t at ");
- } else {
- stack.append("$");
- }
- } else {
- tag.append(" ");
- stack.append("$");
- }
- if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) {
- appendMemBucket(tag, mi.pss, mi.name, false);
- }
- appendMemBucket(stack, mi.pss, mi.name, true);
- if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ
- && ((i+1) >= N || memInfos.get(i+1).oomAdj != lastOomAdj)) {
- stack.append("(");
- for (int k=0; k<DUMP_MEM_OOM_ADJ.length; k++) {
- if (DUMP_MEM_OOM_ADJ[k] == mi.oomAdj) {
- stack.append(DUMP_MEM_OOM_LABEL[k]);
- stack.append(":");
- stack.append(DUMP_MEM_OOM_ADJ[k]);
- }
- }
- stack.append(")");
- }
- }
-
- appendMemInfo(fullNativeBuilder, mi);
- if (mi.oomAdj == ProcessList.NATIVE_ADJ) {
- // The short form only has native processes that are >= 512K.
- if (mi.pss >= 512) {
- appendMemInfo(shortNativeBuilder, mi);
- } else {
- extraNativeRam += mi.pss;
- extraNativeMemtrack += mi.memtrack;
- }
- } else {
- // Short form has all other details, but if we have collected RAM
- // from smaller native processes let's dump a summary of that.
- if (extraNativeRam > 0) {
- appendBasicMemEntry(shortNativeBuilder, ProcessList.NATIVE_ADJ,
- -1, extraNativeRam, extraNativeMemtrack, "(Other native)");
- shortNativeBuilder.append('\n');
- extraNativeRam = 0;
- }
- appendMemInfo(fullJavaBuilder, mi);
- }
- }
-
- fullJavaBuilder.append(" ");
- ProcessList.appendRamKb(fullJavaBuilder, totalPss);
- fullJavaBuilder.append(": TOTAL");
- if (totalMemtrack > 0) {
- fullJavaBuilder.append(" (");
- fullJavaBuilder.append(stringifyKBSize(totalMemtrack));
- fullJavaBuilder.append(" memtrack)");
- } else {
- }
- fullJavaBuilder.append("\n");
-
- MemInfoReader memInfo = new MemInfoReader();
- memInfo.readMemInfo();
- final long[] infos = memInfo.getRawInfo();
-
- StringBuilder memInfoBuilder = new StringBuilder(1024);
- Debug.getMemInfo(infos);
- memInfoBuilder.append(" MemInfo: ");
- memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SLAB])).append(" slab, ");
- memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SHMEM])).append(" shmem, ");
- memInfoBuilder.append(stringifyKBSize(
- infos[Debug.MEMINFO_VM_ALLOC_USED])).append(" vm alloc, ");
- memInfoBuilder.append(stringifyKBSize(
- infos[Debug.MEMINFO_PAGE_TABLES])).append(" page tables ");
- memInfoBuilder.append(stringifyKBSize(
- infos[Debug.MEMINFO_KERNEL_STACK])).append(" kernel stack\n");
- memInfoBuilder.append(" ");
- memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_BUFFERS])).append(" buffers, ");
- memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_CACHED])).append(" cached, ");
- memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_MAPPED])).append(" mapped, ");
- memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_FREE])).append(" free\n");
- if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) {
- memInfoBuilder.append(" ZRAM: ");
- memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_ZRAM_TOTAL]));
- memInfoBuilder.append(" RAM, ");
- memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_TOTAL]));
- memInfoBuilder.append(" swap total, ");
- memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_FREE]));
- memInfoBuilder.append(" swap free\n");
- }
- final long[] ksm = getKsmInfo();
- if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0
- || ksm[KSM_VOLATILE] != 0) {
- memInfoBuilder.append(" KSM: ");
- memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARING]));
- memInfoBuilder.append(" saved from shared ");
- memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARED]));
- memInfoBuilder.append("\n ");
- memInfoBuilder.append(stringifyKBSize(ksm[KSM_UNSHARED]));
- memInfoBuilder.append(" unshared; ");
- memInfoBuilder.append(stringifyKBSize(ksm[KSM_VOLATILE]));
- memInfoBuilder.append(" volatile\n");
- }
- memInfoBuilder.append(" Free RAM: ");
- memInfoBuilder.append(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
- + memInfo.getFreeSizeKb()));
- memInfoBuilder.append("\n");
- long kernelUsed = memInfo.getKernelUsedSizeKb();
- final long ionHeap = Debug.getIonHeapsSizeKb();
- final long ionPool = Debug.getIonPoolsSizeKb();
- if (ionHeap >= 0 && ionPool >= 0) {
- final long ionMapped = Debug.getIonMappedSizeKb();
- final long ionUnmapped = ionHeap - ionMapped;
- memInfoBuilder.append(" ION: ");
- memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
- memInfoBuilder.append("\n");
- // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
- // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
- kernelUsed += ionHeap;
- }
- final long gpuUsage = Debug.getGpuTotalUsageKb();
- if (gpuUsage >= 0) {
- memInfoBuilder.append(" GPU: ");
- memInfoBuilder.append(stringifyKBSize(gpuUsage));
- memInfoBuilder.append("\n");
- }
- memInfoBuilder.append(" Used RAM: ");
- memInfoBuilder.append(stringifyKBSize(
- totalPss - cachedPss + kernelUsed));
- memInfoBuilder.append("\n");
- memInfoBuilder.append(" Lost RAM: ");
- memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
- - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- - kernelUsed - memInfo.getZramTotalSizeKb()));
- memInfoBuilder.append("\n");
- Slog.i(TAG, "Low on memory:");
- Slog.i(TAG, shortNativeBuilder.toString());
- Slog.i(TAG, fullJavaBuilder.toString());
- Slog.i(TAG, memInfoBuilder.toString());
-
- StringBuilder dropBuilder = new StringBuilder(1024);
- /*
- StringWriter oomSw = new StringWriter();
- PrintWriter oomPw = new FastPrintWriter(oomSw, false, 256);
- StringWriter catSw = new StringWriter();
- PrintWriter catPw = new FastPrintWriter(catSw, false, 256);
- String[] emptyArgs = new String[] { };
- dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw);
- oomPw.flush();
- String oomString = oomSw.toString();
- */
- dropBuilder.append("Low on memory:");
- dropBuilder.append(stack);
- dropBuilder.append('\n');
- dropBuilder.append(fullNativeBuilder);
- dropBuilder.append(fullJavaBuilder);
- dropBuilder.append('\n');
- dropBuilder.append(memInfoBuilder);
- dropBuilder.append('\n');
- /*
- dropBuilder.append(oomString);
- dropBuilder.append('\n');
- */
- StringWriter catSw = new StringWriter();
- synchronized (ActivityManagerService.this) {
- PrintWriter catPw = new FastPrintWriter(catSw, false, 256);
- String[] emptyArgs = new String[] { };
- catPw.println();
- dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null, -1);
- catPw.println();
- mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0,
- false, null).dumpLocked();
- catPw.println();
- mAtmInternal.dump(DUMP_ACTIVITIES_CMD, null, catPw, emptyArgs, 0, false, false, null);
- catPw.flush();
- }
- dropBuilder.append(catSw.toString());
- FrameworkStatsLog.write(FrameworkStatsLog.LOW_MEM_REPORTED);
- addErrorToDropBox("lowmem", null, "system_server", null,
- null, null, tag.toString(), dropBuilder.toString(), null, null);
- //Slog.i(TAG, "Sent to dropbox:");
- //Slog.i(TAG, dropBuilder.toString());
- synchronized (ActivityManagerService.this) {
- long now = SystemClock.uptimeMillis();
- if (mLastMemUsageReportTime < now) {
- mLastMemUsageReportTime = now;
- }
- }
- }
-
/**
* Searches array of arguments for the specified string
* @param args array of argument strings
@@ -12185,7 +11151,6 @@
ProcessList.remove(app.pid);
}
- mProcessesToGc.remove(app);
mAppProfiler.onCleanupApplicationRecordLocked(app);
// Dismiss any open dialogs.
@@ -12285,15 +11250,7 @@
});
}
- for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
- ProcessChangeItem item = mPendingProcessChanges.get(i);
- if (app.pid > 0 && item.pid == app.pid) {
- mPendingProcessChanges.remove(i);
- mAvailProcessChanges.add(item);
- }
- }
- mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid,
- null).sendToTarget();
+ mProcessList.scheduleDispatchProcessDiedLocked(app.pid, app.info.uid);
// If this is a precede instance of another process instance
allowRestart = true;
@@ -12626,7 +11583,7 @@
Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
throw new IllegalArgumentException("Invalid service token");
}
- mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
+ mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false);
}
}
@@ -12938,9 +11895,9 @@
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
- if (callerApp.info.uid != SYSTEM_UID &&
- !callerApp.pkgList.containsKey(callerPackage) &&
- !"android".equals(callerPackage)) {
+ if (callerApp.info.uid != SYSTEM_UID
+ && !callerApp.getPkgList().containsKey(callerPackage)
+ && !"android".equals(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
@@ -14385,11 +13342,12 @@
|| (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
boolean disableTestApiChecks = disableHiddenApiChecks
|| (flags & INSTR_FLAG_DISABLE_TEST_API_CHECKS) != 0;
+
if (disableHiddenApiChecks || disableTestApiChecks) {
enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
"disable hidden API checks");
- enableTestApiAccess(ii.packageName);
+ enableTestApiAccess(ai.packageName);
}
final long origId = Binder.clearCallingIdentity();
@@ -14406,11 +13364,10 @@
mUsageStatsService.reportEvent(ii.targetPackage, userId,
UsageEvents.Event.SYSTEM_INTERACTION);
}
- app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
- disableTestApiChecks, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY);
+ app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride,
+ ZYGOTE_POLICY_FLAG_EMPTY);
}
-
app.setActiveInstrumentation(activeInstr);
activeInstr.mFinished = false;
activeInstr.mSourceUid = callingUid;
@@ -14509,8 +13466,11 @@
return;
}
final long origId = Binder.clearCallingIdentity();
- addInstrumentationResultsLocked(app, results);
- Binder.restoreCallingIdentity(origId);
+ try {
+ addInstrumentationResultsLocked(app, results);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
}
@@ -14542,7 +13502,7 @@
mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
app.info.packageName, AppOpsManager.MODE_ERRORED);
mAppOpsService.setAppOpsServiceDelegate(null);
- getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation();
+ getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
instr.mUiAutomationConnection).sendToTarget();
}
@@ -14813,28 +13773,9 @@
}
/**
- * Ask a given process to GC right now.
- */
- final void performAppGcLocked(ProcessRecord app) {
- try {
- app.lastRequestedGc = SystemClock.uptimeMillis();
- if (app.thread != null) {
- if (app.reportLowMemory) {
- app.reportLowMemory = false;
- app.thread.scheduleLowMemory();
- } else {
- app.thread.processInBackground();
- }
- }
- } catch (Exception e) {
- // whatever.
- }
- }
-
- /**
* Returns true if things are idle enough to perform GCs.
*/
- private final boolean canGcNowLocked() {
+ final boolean canGcNowLocked() {
for (BroadcastQueue q : mBroadcastQueues) {
if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isEmpty()) {
return false;
@@ -14843,107 +13784,6 @@
return mAtmInternal.canGcNow();
}
- /**
- * Perform GCs on all processes that are waiting for it, but only
- * if things are idle.
- */
- final void performAppGcsLocked() {
- final int N = mProcessesToGc.size();
- if (N <= 0) {
- return;
- }
- if (canGcNowLocked()) {
- while (mProcessesToGc.size() > 0) {
- ProcessRecord proc = mProcessesToGc.remove(0);
- if (proc.getCurRawAdj() > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
- if ((proc.lastRequestedGc+mConstants.GC_MIN_INTERVAL)
- <= SystemClock.uptimeMillis()) {
- // To avoid spamming the system, we will GC processes one
- // at a time, waiting a few seconds between each.
- performAppGcLocked(proc);
- scheduleAppGcsLocked();
- return;
- } else {
- // It hasn't been long enough since we last GCed this
- // process... put it in the list to wait for its time.
- addProcessToGcListLocked(proc);
- break;
- }
- }
- }
-
- scheduleAppGcsLocked();
- }
- }
-
- /**
- * If all looks good, perform GCs on all processes waiting for them.
- */
- final void performAppGcsIfAppropriateLocked() {
- if (canGcNowLocked()) {
- performAppGcsLocked();
- return;
- }
- // Still not idle, wait some more.
- scheduleAppGcsLocked();
- }
-
- /**
- * Schedule the execution of all pending app GCs.
- */
- final void scheduleAppGcsLocked() {
- mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG);
-
- if (mProcessesToGc.size() > 0) {
- // Schedule a GC for the time to the next process.
- ProcessRecord proc = mProcessesToGc.get(0);
- Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);
-
- long when = proc.lastRequestedGc + mConstants.GC_MIN_INTERVAL;
- long now = SystemClock.uptimeMillis();
- if (when < (now+mConstants.GC_TIMEOUT)) {
- when = now + mConstants.GC_TIMEOUT;
- }
- mHandler.sendMessageAtTime(msg, when);
- }
- }
-
- /**
- * Add a process to the array of processes waiting to be GCed. Keeps the
- * list in sorted order by the last GC time. The process can't already be
- * on the list.
- */
- final void addProcessToGcListLocked(ProcessRecord proc) {
- boolean added = false;
- for (int i=mProcessesToGc.size()-1; i>=0; i--) {
- if (mProcessesToGc.get(i).lastRequestedGc <
- proc.lastRequestedGc) {
- added = true;
- mProcessesToGc.add(i+1, proc);
- break;
- }
- }
- if (!added) {
- mProcessesToGc.add(0, proc);
- }
- }
-
- /**
- * Set up to ask a process to GC itself. This will either do it
- * immediately, or put it on the list of processes to gc the next
- * time things are idle.
- */
- final void scheduleAppGcLocked(ProcessRecord app) {
- long now = SystemClock.uptimeMillis();
- if ((app.lastRequestedGc+mConstants.GC_MIN_INTERVAL) > now) {
- return;
- }
- if (!mProcessesToGc.contains(app)) {
- addProcessToGcListLocked(app);
- scheduleAppGcsLocked();
- }
- }
-
private void checkExcessivePowerUsage() {
updateCpuStatsNow();
@@ -14972,22 +13812,25 @@
} else {
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4;
}
- if (app.lastCpuTime > 0) {
- final long cputimeUsed = app.curCpuTime - app.lastCpuTime;
- if (checkExcessivePowerUsageLocked(uptimeSince, doCpuKills, cputimeUsed,
- app.processName, app.toShortString(), cpuLimit, app)) {
- app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
- + " dur=" + checkDur + " limit=" + cpuLimit,
- ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
- ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
- true);
- synchronized (mProcessStats.mLock) {
- app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList);
+ synchronized (mAppProfiler.mProfilerLock) {
+ final ProcessProfileRecord profile = app.mProfile;
+ final long curCpuTime = profile.mCurCpuTime.get();
+ final long lastCpuTime = profile.mLastCpuTime.get();
+ if (lastCpuTime > 0) {
+ final long cputimeUsed = curCpuTime - lastCpuTime;
+ if (checkExcessivePowerUsageLocked(uptimeSince, doCpuKills, cputimeUsed,
+ app.processName, app.toShortString(), cpuLimit, app)) {
+ app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
+ + " dur=" + checkDur + " limit=" + cpuLimit,
+ ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
+ ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
+ true);
+ profile.reportExcessiveCpu();
}
}
+ profile.mLastCpuTime.set(curCpuTime);
}
- app.lastCpuTime = app.curCpuTime;
// Also check the phantom processes if there is any
final long chkDur = checkDur;
@@ -15036,15 +13879,14 @@
if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) {
mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName,
uptimeSince, cputimeUsed);
- for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg);
+ app.getPkgList().forEachPackageProcessStats(holder -> {
FrameworkStatsLog.write(
FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
app.info.uid,
processName,
holder.state.getPackage(),
holder.appVersion);
- }
+ });
return true;
}
}
@@ -15056,7 +13898,7 @@
if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
return false;
}
- return getPackageManagerInternalLocked().isPackageEphemeral(
+ return getPackageManagerInternal().isPackageEphemeral(
UserHandle.getUserId(uid), packages[0]);
}
@@ -15106,12 +13948,9 @@
final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
synchronized (mProcessStats.mLock) {
- if (proc.thread != null && proc.baseProcessTracker != null) {
- final int procState = proc.getReportedProcState();
- if (procState != PROCESS_STATE_NONEXISTENT) {
- proc.baseProcessTracker.setState(
- procState, memFactor, now, proc.pkgList.mPkgList);
- }
+ if (proc.thread != null) {
+ proc.mProfile.setProcessTrackerState(
+ proc.getReportedProcState(), memFactor, now);
}
}
}
@@ -15148,7 +13987,8 @@
}
proc.setReportedForegroundServiceTypes(fgServiceTypes);
- ProcessChangeItem item = enqueueProcessChangeItemLocked(proc.pid, proc.info.uid);
+ ProcessChangeItem item = mProcessList.enqueueProcessChangeItemLocked(
+ proc.pid, proc.info.uid);
item.changes |= ProcessChangeItem.CHANGE_FOREGROUND_SERVICES;
item.foregroundServiceTypes = fgServiceTypes;
}
@@ -15159,7 +13999,7 @@
// TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
// the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
- ProcessRecord getTopAppLocked() {
+ ProcessRecord getTopApp() {
final WindowProcessController wpc = mAtmInternal != null ? mAtmInternal.getTopApp() : null;
final ProcessRecord r = wpc != null ? (ProcessRecord) wpc.mOwner : null;
String pkg;
@@ -15172,25 +14012,27 @@
uid = -1;
}
// Has the UID or resumed package name changed?
- if (uid != mCurResumedUid || (pkg != mCurResumedPackage
- && (pkg == null || !pkg.equals(mCurResumedPackage)))) {
+ synchronized (mCurResumedAppLock) {
+ if (uid != mCurResumedUid || (pkg != mCurResumedPackage
+ && (pkg == null || !pkg.equals(mCurResumedPackage)))) {
- final long identity = Binder.clearCallingIdentity();
- try {
- if (mCurResumedPackage != null) {
- mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH,
- mCurResumedPackage, mCurResumedUid);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mCurResumedPackage != null) {
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH,
+ mCurResumedPackage, mCurResumedUid);
+ }
+ mCurResumedPackage = pkg;
+ mCurResumedUid = uid;
+ if (mCurResumedPackage != null) {
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START,
+ mCurResumedPackage, mCurResumedUid);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mCurResumedPackage = pkg;
- mCurResumedUid = uid;
- if (mCurResumedPackage != null) {
- mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START,
- mCurResumedPackage, mCurResumedUid);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
+
}
-
}
return r;
}
@@ -15279,47 +14121,49 @@
userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "makePackageIdle", null);
final long callingId = Binder.clearCallingIdentity();
- synchronized(this) {
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ int pkgUid = -1;
try {
- IPackageManager pm = AppGlobals.getPackageManager();
- int pkgUid = -1;
- try {
- pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES
- | MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM);
- } catch (RemoteException e) {
- }
- if (pkgUid == -1) {
- throw new IllegalArgumentException("Unknown package name " + packageName);
- }
+ pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES
+ | MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM);
+ } catch (RemoteException e) {
+ }
+ if (pkgUid == -1) {
+ throw new IllegalArgumentException("Unknown package name " + packageName);
+ }
- if (mLocalPowerManager != null) {
- mLocalPowerManager.startUidChanges();
- }
- final int appId = UserHandle.getAppId(pkgUid);
- final int N = mProcessList.mActiveUids.size();
- for (int i = N - 1; i >= 0; i--) {
- final UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
- final long bgTime = uidRec.lastBackgroundTime;
- if (bgTime > 0 && !uidRec.idle) {
- if (UserHandle.getAppId(uidRec.uid) == appId) {
- if (userId == UserHandle.USER_ALL ||
- userId == UserHandle.getUserId(uidRec.uid)) {
- EventLogTags.writeAmUidIdle(uidRec.uid);
- uidRec.idle = true;
- uidRec.setIdle = true;
- Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid)
- + " from package " + packageName + " user " + userId);
- doStopUidLocked(uidRec.uid, uidRec);
+ synchronized (this) {
+ try {
+ if (mLocalPowerManager != null) {
+ mLocalPowerManager.startUidChanges();
+ }
+ final int appId = UserHandle.getAppId(pkgUid);
+ for (int i = mProcessList.mActiveUids.size() - 1; i >= 0; i--) {
+ final UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
+ final long bgTime = uidRec.lastBackgroundTime;
+ if (bgTime > 0 && !uidRec.idle) {
+ if (UserHandle.getAppId(uidRec.uid) == appId) {
+ if (userId == UserHandle.USER_ALL
+ || userId == UserHandle.getUserId(uidRec.uid)) {
+ EventLogTags.writeAmUidIdle(uidRec.uid);
+ uidRec.idle = true;
+ uidRec.setIdle = true;
+ Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid)
+ + " from package " + packageName + " user " + userId);
+ doStopUidLocked(uidRec.uid, uidRec);
+ }
}
}
}
+ } finally {
+ if (mLocalPowerManager != null) {
+ mLocalPowerManager.finishUidChanges();
+ }
}
- } finally {
- if (mLocalPowerManager != null) {
- mLocalPowerManager.finishUidChanges();
- }
- Binder.restoreCallingIdentity(callingId);
}
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
}
@@ -15373,24 +14217,24 @@
}
/**
- * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
+ * Allowlists {@code targetUid} to temporarily bypass Power Save mode.
*/
@GuardedBy("this")
- void tempWhitelistForPendingIntentLocked(int callerPid, int callerUid, int targetUid,
+ void tempAllowlistForPendingIntentLocked(int callerPid, int callerUid, int targetUid,
long duration, int type, String tag) {
if (DEBUG_WHITELISTS) {
- Slog.d(TAG, "tempWhitelistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", "
+ Slog.d(TAG, "tempAllowlistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", "
+ targetUid + ", " + duration + ", " + type + ")");
}
synchronized (mPidsSelfLocked) {
final ProcessRecord pr = mPidsSelfLocked.get(callerPid);
if (pr == null) {
- Slog.w(TAG, "tempWhitelistForPendingIntentLocked() no ProcessRecord for pid "
+ Slog.w(TAG, "tempAllowlistForPendingIntentLocked() no ProcessRecord for pid "
+ callerPid);
return;
}
- if (!pr.whitelistManager) {
+ if (!pr.mAllowlistManager) {
if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid)
!= PackageManager.PERMISSION_GRANTED
&& checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callerPid, callerUid)
@@ -15398,7 +14242,7 @@
&& checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callerPid,
callerUid) != PackageManager.PERMISSION_GRANTED) {
if (DEBUG_WHITELISTS) {
- Slog.d(TAG, "tempWhitelistForPendingIntentLocked() for target " + targetUid
+ Slog.d(TAG, "tempAllowlistForPendingIntentLocked() for target " + targetUid
+ ": pid " + callerPid + " is not allowed");
}
return;
@@ -15406,35 +14250,35 @@
}
}
- tempWhitelistUidLocked(targetUid, duration, tag, type);
+ tempAllowlistUidLocked(targetUid, duration, tag, type);
}
/**
- * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
+ * Allowlists {@code targetUid} to temporarily bypass Power Save mode.
*/
@GuardedBy("this")
- void tempWhitelistUidLocked(int targetUid, long duration, String tag, int type) {
- mPendingTempWhitelist.put(targetUid,
- new PendingTempWhitelist(targetUid, duration, tag, type));
- setUidTempWhitelistStateLocked(targetUid, true);
- mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget();
+ void tempAllowlistUidLocked(int targetUid, long duration, String tag, int type) {
+ mPendingTempAllowlist.put(targetUid,
+ new PendingTempAllowlist(targetUid, duration, tag, type));
+ setUidTempAllowlistStateLocked(targetUid, true);
+ mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget();
if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
mFgsStartTempAllowList.add(targetUid, duration);
}
}
- void pushTempWhitelist() {
+ void pushTempAllowlist() {
final int N;
- final PendingTempWhitelist[] list;
+ final PendingTempAllowlist[] list;
// First copy out the pending changes... we need to leave them in the map for now,
// in case someone needs to check what is coming up while we don't have the lock held.
synchronized(this) {
- N = mPendingTempWhitelist.size();
- list = new PendingTempWhitelist[N];
+ N = mPendingTempAllowlist.size();
+ list = new PendingTempAllowlist[N];
for (int i = 0; i < N; i++) {
- list[i] = mPendingTempWhitelist.valueAt(i);
+ list[i] = mPendingTempAllowlist.valueAt(i);
}
}
@@ -15443,7 +14287,7 @@
// device idle policy anyway at this phase.
if (mLocalDeviceIdleController != null) {
for (int i = 0; i < N; i++) {
- PendingTempWhitelist ptw = list[i];
+ PendingTempAllowlist ptw = list[i];
mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
ptw.duration, ptw.type, true, ptw.tag);
}
@@ -15452,23 +14296,23 @@
// And now we can safely remove them from the map.
synchronized(this) {
for (int i = 0; i < N; i++) {
- PendingTempWhitelist ptw = list[i];
- int index = mPendingTempWhitelist.indexOfKey(ptw.targetUid);
- if (index >= 0 && mPendingTempWhitelist.valueAt(index) == ptw) {
- mPendingTempWhitelist.removeAt(index);
+ PendingTempAllowlist ptw = list[i];
+ int index = mPendingTempAllowlist.indexOfKey(ptw.targetUid);
+ if (index >= 0 && mPendingTempAllowlist.valueAt(index) == ptw) {
+ mPendingTempAllowlist.removeAt(index);
}
}
}
}
@GuardedBy("this")
- final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) {
- mOomAdjuster.setAppIdTempWhitelistStateLocked(uid, onWhitelist);
+ final void setAppIdTempAllowlistStateLocked(int uid, boolean onAllowlist) {
+ mOomAdjuster.setAppIdTempAllowlistStateLocked(uid, onAllowlist);
}
@GuardedBy("this")
- final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
- mOomAdjuster.setUidTempWhitelistStateLocked(uid, onWhitelist);
+ final void setUidTempAllowlistStateLocked(int uid, boolean onAllowlist) {
+ mOomAdjuster.setUidTempAllowlistStateLocked(uid, onAllowlist);
}
private void trimApplications(boolean forceFullOomAdj, String oomAdjReason) {
@@ -15530,13 +14374,13 @@
throw new SecurityException("Only SIGNAL_USR1 is allowed");
}
- synchronized (this) {
- if (checkCallingPermission(android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES);
- }
+ if (checkCallingPermission(android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES);
+ }
+ synchronized (this) {
for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mProcessList.mLruProcesses.get(i);
if (r.thread != null && r.isPersistent()) {
@@ -15548,20 +14392,20 @@
public boolean profileControl(String process, int userId, boolean start,
ProfilerInfo profilerInfo, int profileType) throws RemoteException {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission.
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
+ if (start && (profilerInfo == null || profilerInfo.profileFd == null)) {
+ throw new IllegalArgumentException("null profile info or fd");
+ }
+
+ ProcessRecord proc = null;
synchronized (this) {
- // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
- // its own permission.
- if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.SET_ACTIVITY_WATCHER);
- }
-
- if (start && (profilerInfo == null || profilerInfo.profileFd == null)) {
- throw new IllegalArgumentException("null profile info or fd");
- }
-
- ProcessRecord proc = null;
if (process != null) {
proc = findProcessLocked(process, userId, "profileControl");
}
@@ -15569,8 +14413,9 @@
if (start && (proc == null || proc.thread == null)) {
throw new IllegalArgumentException("Unknown process: " + process);
}
-
- return mAppProfiler.profileControlLocked(proc, start, profilerInfo, profileType);
+ }
+ synchronized (mAppProfiler.mProfilerLock) {
+ return mAppProfiler.profileControlLPf(proc, start, profilerInfo, profileType);
}
}
@@ -15612,19 +14457,19 @@
boolean runGc, String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) {
try {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission (same as profileControl).
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
+ if (fd == null) {
+ throw new IllegalArgumentException("null fd");
+ }
+
synchronized (this) {
- // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
- // its own permission (same as profileControl).
- if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.SET_ACTIVITY_WATCHER);
- }
-
- if (fd == null) {
- throw new IllegalArgumentException("null fd");
- }
-
ProcessRecord proc = findProcessLocked(process, userId, "dumpHeap");
if (proc == null || proc.thread == null) {
throw new IllegalArgumentException("Unknown process: " + process);
@@ -15683,7 +14528,7 @@
}
processName = proc.processName;
uid = proc.uid;
- if (reportPackage != null && !proc.pkgList.containsKey(reportPackage)) {
+ if (reportPackage != null && !proc.getPkgList().containsKey(reportPackage)) {
throw new SecurityException("Package " + reportPackage + " is not running in "
+ proc);
}
@@ -15871,16 +14716,16 @@
}
public boolean startBinderTracking() throws RemoteException {
+ // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
+ // permission (same as profileControl).
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
synchronized (this) {
mBinderTransactionTrackingEnabled = true;
- // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
- // permission (same as profileControl).
- if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.SET_ACTIVITY_WATCHER);
- }
-
for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
ProcessRecord process = mProcessList.mLruProcesses.get(i);
if (!processSanityChecksLocked(process)) {
@@ -15898,19 +14743,19 @@
public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException {
try {
- synchronized (this) {
- mBinderTransactionTrackingEnabled = false;
- // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
- // permission (same as profileControl).
- if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.SET_ACTIVITY_WATCHER);
- }
+ // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
+ // permission (same as profileControl).
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+ synchronized (this) {
if (fd == null) {
throw new IllegalArgumentException("null fd");
}
+ mBinderTransactionTrackingEnabled = false;
PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
pw.println("Binder transaction traces for all processes.\n");
@@ -16059,22 +14904,22 @@
@Override
public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) {
synchronized (ActivityManagerService.this) {
- mDeviceIdleWhitelist = allAppids;
- mDeviceIdleExceptIdleWhitelist = exceptIdleAppids;
+ mDeviceIdleAllowlist = allAppids;
+ mDeviceIdleExceptIdleAllowlist = exceptIdleAppids;
}
}
@Override
public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding,
- long durationMs, @BroadcastOptions.TempAllowListType int type) {
+ long durationMs, @TempAllowListType int type) {
synchronized (ActivityManagerService.this) {
- mDeviceIdleTempWhitelist = appids;
+ mDeviceIdleTempAllowlist = appids;
if (adding) {
if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
mFgsStartTempAllowList.add(changingUid, durationMs);
}
}
- setAppIdTempWhitelistStateLocked(changingUid, adding);
+ setAppIdTempAllowlistStateLocked(changingUid, adding);
}
}
@@ -16301,26 +15146,20 @@
@Override
public void updateCpuStats() {
- synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.updateCpuStatsLocked();
- }
+ ActivityManagerService.this.updateCpuStats();
}
@Override
public void updateBatteryStats(ComponentName activity, int uid, int userId,
boolean resumed) {
- synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.updateBatteryStats(activity, uid, userId, resumed);
- }
+ ActivityManagerService.this.updateBatteryStats(activity, uid, userId, resumed);
}
@Override
public void updateActivityUsageStats(ComponentName activity, int userId, int event,
IBinder appToken, ComponentName taskRoot) {
- synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.updateActivityUsageStats(activity, userId, event,
- appToken, taskRoot);
- }
+ ActivityManagerService.this.updateActivityUsageStats(activity, userId, event,
+ appToken, taskRoot);
}
@Override
@@ -16397,16 +15236,14 @@
@Override
public void scheduleAppGcs() {
- synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.scheduleAppGcsLocked();
+ synchronized (mAppProfiler.mProfilerLock) {
+ mAppProfiler.scheduleAppGcsLPf();
}
}
@Override
public int getTaskIdForActivity(IBinder token, boolean onlyRoot) {
- synchronized (ActivityManagerService.this) {
- return ActivityManagerService.this.getTaskForActivity(token, onlyRoot);
- }
+ return ActivityManagerService.this.getTaskForActivity(token, onlyRoot);
}
@Override
@@ -16446,7 +15283,7 @@
public void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
long duration, int type, String tag) {
synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.tempWhitelistForPendingIntentLocked(
+ ActivityManagerService.this.tempAllowlistForPendingIntentLocked(
callerPid, callerUid, targetUid, duration, type, tag);
}
}
@@ -16527,7 +15364,8 @@
(ActivityServiceConnectionsHolder) connectionHolder;
synchronized (ActivityManagerService.this) {
holder.forEachConnection(cr -> mServices.removeConnectionLocked(
- (ConnectionRecord) cr, null /* skipApp */, holder /* skipAct */));
+ (ConnectionRecord) cr, null /* skipApp */, holder /* skipAct */,
+ false /* enqueueOomAdj */));
}
}
@@ -16872,14 +15710,11 @@
throw new SecurityException("Requires permission " + FILTER_EVENTS);
}
ProcessRecord proc;
- long timeoutMillis;
- synchronized (this) {
- synchronized (mPidsSelfLocked) {
- proc = mPidsSelfLocked.get(pid);
- }
- timeoutMillis = proc != null ? proc.getInputDispatchingTimeoutMillis() :
- DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
}
+ final long timeoutMillis = proc != null ? proc.getInputDispatchingTimeoutMillis() :
+ DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {
return 0;
@@ -17093,10 +15928,7 @@
* resources and overlaid values are available immediately.
*/
public void updateSystemUiContext() {
- PackageManagerInternal packageManagerInternal;
- synchronized (this) {
- packageManagerInternal = getPackageManagerInternalLocked();
- }
+ final PackageManagerInternal packageManagerInternal = getPackageManagerInternal();
ApplicationInfo ai = packageManagerInternal.getApplicationInfo("android",
GET_SHARED_LIBRARY_FILES, Binder.getCallingUid(), UserHandle.USER_SYSTEM);
@@ -17279,10 +16111,8 @@
* like persisting database etc.
*/
public void prepareForPossibleShutdown() {
- synchronized (this) {
- if (mUsageStatsService != null) {
- mUsageStatsService.prepareForPossibleShutdown();
- }
+ if (mUsageStatsService != null) {
+ mUsageStatsService.prepareForPossibleShutdown();
}
}
@@ -17371,7 +16201,7 @@
final String packageName = instr.mTargetInfo.packageName;
final List<String> permissionNames = permissions != null ?
Arrays.asList(permissions) : null;
- getPermissionManagerInternalLocked().startShellPermissionIdentityDelegation(
+ getPermissionManagerInternal().startShellPermissionIdentityDelegation(
delegateUid, packageName, permissionNames);
return;
}
@@ -17386,7 +16216,7 @@
}
synchronized (ActivityManagerService.this) {
mAppOpsService.setAppOpsServiceDelegate(null);
- getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation();
+ getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
}
}
@@ -17525,7 +16355,7 @@
public void setActivityLocusContext(ComponentName activity, LocusId locusId, IBinder appToken) {
final int callingUid = Binder.getCallingUid();
final int userId = UserHandle.getCallingUserId();
- if (getPackageManagerInternalLocked().getPackageUid(activity.getPackageName(),
+ if (getPackageManagerInternal().getPackageUid(activity.getPackageName(),
/*flags=*/ 0, userId) != callingUid) {
throw new SecurityException("Calling uid " + callingUid + " cannot set locusId"
+ "for package " + activity.getPackageName());
@@ -17555,9 +16385,7 @@
@Override
public void resetAppErrors() {
enforceCallingPermission(Manifest.permission.RESET_APP_ERRORS, "resetAppErrors");
- synchronized (this) {
- mAppErrors.resetStateLocked();
- }
+ mAppErrors.resetState();
}
@Override
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index d76e2d7..779d378 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -71,8 +71,8 @@
BidiFormatter bidi = BidiFormatter.getInstance();
CharSequence name;
- if ((mProc.pkgList.size() == 1) &&
- (name = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
+ if ((mProc.getPkgList().size() == 1)
+ && (name = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
setTitle(res.getString(
data.repeating ? com.android.internal.R.string.aerr_application_repeated
: com.android.internal.R.string.aerr_application,
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 35f4689..7be54c2 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -52,6 +52,7 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -74,27 +75,32 @@
private final Context mContext;
private final PackageWatchdog mPackageWatchdog;
+ @GuardedBy("mBadProcessLock")
private ArraySet<String> mAppsNotReportingCrashes;
/**
* The last time that various processes have crashed since they were last explicitly started.
*/
+ @GuardedBy("mBadProcessLock")
private final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<>();
/**
* The last time that various processes have crashed (not reset even when explicitly started).
*/
+ @GuardedBy("mBadProcessLock")
private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>();
/**
* The last time that various processes have crashed and shown an error dialog.
*/
+ @GuardedBy("mBadProcessLock")
private final ProcessMap<Long> mProcessCrashShowDialogTimes = new ProcessMap<>();
/**
* A pairing between how many times various processes have crashed since a given time.
* Entry and exit conditions for this map are similar to mProcessCrashTimes.
*/
+ @GuardedBy("mBadProcessLock")
private final ProcessMap<Pair<Long, Integer>> mProcessCrashCounts = new ProcessMap<>();
/**
@@ -116,6 +122,16 @@
* lock operations from the simple lookup cases.
*/
private volatile ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>();
+
+ /**
+ * Dedicated lock for {@link #mAppsNotReportingCrashes}, {@link #mProcessCrashTimes},
+ * {@link #mProcessCrashTimesPersistent}, {@link #mProcessCrashShowDialogTimes},
+ * {@link #mProcessCrashCounts} and {@link #mBadProcesses}.
+ *
+ * <p>The naming convention of the function with this lock should be "-LBp"</b>
+ *
+ * @See mBadProcesses
+ */
private final Object mBadProcessLock = new Object();
AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog) {
@@ -126,147 +142,152 @@
}
/** Resets the current state but leaves the constructor-provided fields unchanged. */
- public void resetStateLocked() {
+ public void resetState() {
Slog.i(TAG, "Resetting AppErrors");
- mAppsNotReportingCrashes.clear();
- mProcessCrashTimes.clear();
- mProcessCrashTimesPersistent.clear();
- mProcessCrashShowDialogTimes.clear();
- mProcessCrashCounts.clear();
synchronized (mBadProcessLock) {
+ mAppsNotReportingCrashes.clear();
+ mProcessCrashTimes.clear();
+ mProcessCrashTimesPersistent.clear();
+ mProcessCrashShowDialogTimes.clear();
+ mProcessCrashCounts.clear();
mBadProcesses = new ProcessMap<>();
}
}
void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) {
- final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses;
- if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) {
- return;
- }
-
- final long token = proto.start(fieldId);
- final long now = SystemClock.uptimeMillis();
- proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
-
- if (!mProcessCrashTimes.getMap().isEmpty()) {
- final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
- final int procCount = pmap.size();
- for (int ip = 0; ip < procCount; ip++) {
- final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES);
- final String pname = pmap.keyAt(ip);
- final SparseArray<Long> uids = pmap.valueAt(ip);
- final int uidCount = uids.size();
-
- proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
- for (int i = 0; i < uidCount; i++) {
- final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.getProcessNames().get(pname, puid);
- if (dumpPackage != null
- && (r == null || !r.pkgList.containsKey(dumpPackage))) {
- continue;
- }
- final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES);
- proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid);
- proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS,
- uids.valueAt(i));
- proto.end(etoken);
- }
- proto.end(ctoken);
+ synchronized (mBadProcessLock) {
+ final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses;
+ if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) {
+ return;
}
- }
+ final long token = proto.start(fieldId);
+ final long now = SystemClock.uptimeMillis();
+ proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
- if (!badProcesses.getMap().isEmpty()) {
- final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap();
- final int processCount = pmap.size();
- for (int ip = 0; ip < processCount; ip++) {
- final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
- final String pname = pmap.keyAt(ip);
- final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
- final int uidCount = uids.size();
+ if (!mProcessCrashTimes.getMap().isEmpty()) {
+ final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+ final int procCount = pmap.size();
+ for (int ip = 0; ip < procCount; ip++) {
+ final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES);
+ final String pname = pmap.keyAt(ip);
+ final SparseArray<Long> uids = pmap.valueAt(ip);
+ final int uidCount = uids.size();
- proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
- for (int i = 0; i < uidCount; i++) {
- final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.getProcessNames().get(pname, puid);
- if (dumpPackage != null && (r == null
- || !r.pkgList.containsKey(dumpPackage))) {
- continue;
+ proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
+ for (int i = 0; i < uidCount; i++) {
+ final int puid = uids.keyAt(i);
+ final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+ if (dumpPackage != null
+ && (r == null || !r.getPkgList().containsKey(dumpPackage))) {
+ continue;
+ }
+ final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES);
+ proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid);
+ proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS,
+ uids.valueAt(i));
+ proto.end(etoken);
}
- final BadProcessInfo info = uids.valueAt(i);
- final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
- proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
- proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
- proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
- proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
- proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
- proto.end(etoken);
+ proto.end(ctoken);
}
- proto.end(btoken);
- }
- }
- proto.end(token);
+ }
+
+ if (!badProcesses.getMap().isEmpty()) {
+ final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap();
+ final int processCount = pmap.size();
+ for (int ip = 0; ip < processCount; ip++) {
+ final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
+ final String pname = pmap.keyAt(ip);
+ final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
+ final int uidCount = uids.size();
+
+ proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
+ for (int i = 0; i < uidCount; i++) {
+ final int puid = uids.keyAt(i);
+ final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+ if (dumpPackage != null && (r == null
+ || !r.getPkgList().containsKey(dumpPackage))) {
+ continue;
+ }
+ final BadProcessInfo info = uids.valueAt(i);
+ final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
+ proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
+ proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
+ proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
+ proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
+ proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
+ proto.end(etoken);
+ }
+ proto.end(btoken);
+ }
+ }
+
+ proto.end(token);
+ }
}
boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) {
final long now = SystemClock.uptimeMillis();
- if (!mProcessCrashTimes.getMap().isEmpty()) {
- boolean printed = false;
- final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
- final int processCount = pmap.size();
- for (int ip = 0; ip < processCount; ip++) {
- final String pname = pmap.keyAt(ip);
- final SparseArray<Long> uids = pmap.valueAt(ip);
- final int uidCount = uids.size();
- for (int i = 0; i < uidCount; i++) {
- final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.getProcessNames().get(pname, puid);
- if (dumpPackage != null && (r == null
- || !r.pkgList.containsKey(dumpPackage))) {
- continue;
+ synchronized (mBadProcessLock) {
+ if (!mProcessCrashTimes.getMap().isEmpty()) {
+ boolean printed = false;
+ final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+ final int processCount = pmap.size();
+ for (int ip = 0; ip < processCount; ip++) {
+ final String pname = pmap.keyAt(ip);
+ final SparseArray<Long> uids = pmap.valueAt(ip);
+ final int uidCount = uids.size();
+ for (int i = 0; i < uidCount; i++) {
+ final int puid = uids.keyAt(i);
+ final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+ if (dumpPackage != null && (r == null
+ || !r.getPkgList().containsKey(dumpPackage))) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println();
+ needSep = true;
+ pw.println(" Time since processes crashed:");
+ printed = true;
+ }
+ pw.print(" Process "); pw.print(pname);
+ pw.print(" uid "); pw.print(puid);
+ pw.print(": last crashed ");
+ TimeUtils.formatDuration(now - uids.valueAt(i), pw);
+ pw.println(" ago");
}
- if (!printed) {
- if (needSep) pw.println();
- needSep = true;
- pw.println(" Time since processes crashed:");
- printed = true;
- }
- pw.print(" Process "); pw.print(pname);
- pw.print(" uid "); pw.print(puid);
- pw.print(": last crashed ");
- TimeUtils.formatDuration(now-uids.valueAt(i), pw);
- pw.println(" ago");
}
}
- }
- if (!mProcessCrashCounts.getMap().isEmpty()) {
- boolean printed = false;
- final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pmap =
- mProcessCrashCounts.getMap();
- final int processCount = pmap.size();
- for (int ip = 0; ip < processCount; ip++) {
- final String pname = pmap.keyAt(ip);
- final SparseArray<Pair<Long, Integer>> uids = pmap.valueAt(ip);
- final int uidCount = uids.size();
- for (int i = 0; i < uidCount; i++) {
- final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.getProcessNames().get(pname, puid);
- if (dumpPackage != null && (r == null || !r.pkgList.containsKey(dumpPackage))) {
- continue;
+ if (!mProcessCrashCounts.getMap().isEmpty()) {
+ boolean printed = false;
+ final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pmap =
+ mProcessCrashCounts.getMap();
+ final int processCount = pmap.size();
+ for (int ip = 0; ip < processCount; ip++) {
+ final String pname = pmap.keyAt(ip);
+ final SparseArray<Pair<Long, Integer>> uids = pmap.valueAt(ip);
+ final int uidCount = uids.size();
+ for (int i = 0; i < uidCount; i++) {
+ final int puid = uids.keyAt(i);
+ final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+ if (dumpPackage != null
+ && (r == null || !r.getPkgList().containsKey(dumpPackage))) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println();
+ needSep = true;
+ pw.println(" First time processes crashed and counts:");
+ printed = true;
+ }
+ pw.print(" Process "); pw.print(pname);
+ pw.print(" uid "); pw.print(puid);
+ pw.print(": first crashed ");
+ TimeUtils.formatDuration(now - uids.valueAt(i).first, pw);
+ pw.print(" ago; crashes since then: "); pw.println(uids.valueAt(i).second);
}
- if (!printed) {
- if (needSep) pw.println();
- needSep = true;
- pw.println(" First time processes crashed and counts:");
- printed = true;
- }
- pw.print(" Process "); pw.print(pname);
- pw.print(" uid "); pw.print(puid);
- pw.print(": first crashed ");
- TimeUtils.formatDuration(now - uids.valueAt(i).first, pw);
- pw.print(" ago; crashes since then: "); pw.println(uids.valueAt(i).second);
}
}
}
@@ -284,7 +305,7 @@
final int puid = uids.keyAt(i);
final ProcessRecord r = mService.getProcessNames().get(pname, puid);
if (dumpPackage != null && (r == null
- || !r.pkgList.containsKey(dumpPackage))) {
+ || !r.getPkgList().containsKey(dumpPackage))) {
continue;
}
if (!printed) {
@@ -349,33 +370,38 @@
}
}
- void resetProcessCrashTimeLocked(final String processName, final int uid) {
- mProcessCrashTimes.remove(processName, uid);
- mProcessCrashCounts.remove(processName, uid);
+ void resetProcessCrashTime(final String processName, final int uid) {
+ synchronized (mBadProcessLock) {
+ mProcessCrashTimes.remove(processName, uid);
+ mProcessCrashCounts.remove(processName, uid);
+ }
}
- void resetProcessCrashTimeLocked(boolean resetEntireUser, int appId, int userId) {
- final ArrayMap<String, SparseArray<Long>> pTimeMap = mProcessCrashTimes.getMap();
- for (int ip = pTimeMap.size() - 1; ip >= 0; ip--) {
- SparseArray<Long> ba = pTimeMap.valueAt(ip);
- resetProcessCrashMapLocked(ba, resetEntireUser, appId, userId);
- if (ba.size() == 0) {
- pTimeMap.removeAt(ip);
+ void resetProcessCrashTime(boolean resetEntireUser, int appId, int userId) {
+ synchronized (mBadProcessLock) {
+ final ArrayMap<String, SparseArray<Long>> pTimeMap = mProcessCrashTimes.getMap();
+ for (int ip = pTimeMap.size() - 1; ip >= 0; ip--) {
+ SparseArray<Long> ba = pTimeMap.valueAt(ip);
+ resetProcessCrashMapLBp(ba, resetEntireUser, appId, userId);
+ if (ba.size() == 0) {
+ pTimeMap.removeAt(ip);
+ }
}
- }
- final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pCountMap =
- mProcessCrashCounts.getMap();
- for (int ip = pCountMap.size() - 1; ip >= 0; ip--) {
- SparseArray<Pair<Long, Integer>> ba = pCountMap.valueAt(ip);
- resetProcessCrashMapLocked(ba, resetEntireUser, appId, userId);
- if (ba.size() == 0) {
- pCountMap.removeAt(ip);
+ final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pCountMap =
+ mProcessCrashCounts.getMap();
+ for (int ip = pCountMap.size() - 1; ip >= 0; ip--) {
+ SparseArray<Pair<Long, Integer>> ba = pCountMap.valueAt(ip);
+ resetProcessCrashMapLBp(ba, resetEntireUser, appId, userId);
+ if (ba.size() == 0) {
+ pCountMap.removeAt(ip);
+ }
}
}
}
- private void resetProcessCrashMapLocked(SparseArray<?> ba, boolean resetEntireUser,
+ @GuardedBy("mBadProcessLock")
+ private void resetProcessCrashMapLBp(SparseArray<?> ba, boolean resetEntireUser,
int appId, int userId) {
for (int i = ba.size() - 1; i >= 0; i--) {
boolean remove = false;
@@ -399,12 +425,14 @@
}
}
- void loadAppsNotReportingCrashesFromConfigLocked(String appsNotReportingCrashesConfig) {
+ void loadAppsNotReportingCrashesFromConfig(String appsNotReportingCrashesConfig) {
if (appsNotReportingCrashesConfig != null) {
final String[] split = appsNotReportingCrashesConfig.split(",");
if (split.length > 0) {
- mAppsNotReportingCrashes = new ArraySet<>();
- Collections.addAll(mAppsNotReportingCrashes, split);
+ synchronized (mBadProcessLock) {
+ mAppsNotReportingCrashes = new ArraySet<>();
+ Collections.addAll(mAppsNotReportingCrashes, split);
+ }
}
}
}
@@ -465,7 +493,7 @@
proc = p;
break;
}
- if (p.pkgList.containsKey(packageName)
+ if (p.getPkgList().containsKey(packageName)
&& (userId < 0 || p.userId == userId)) {
proc = p;
}
@@ -516,7 +544,7 @@
}
}
- void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
+ private void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
int callingPid, int callingUid) {
long timeMillis = System.currentTimeMillis();
String shortMsg = crashInfo.exceptionClassName;
@@ -599,44 +627,50 @@
if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
res = AppErrorDialog.FORCE_QUIT;
}
- synchronized (mService) {
- if (res == AppErrorDialog.MUTE) {
- stopReportingCrashesLocked(r);
+ if (res == AppErrorDialog.MUTE) {
+ synchronized (mBadProcessLock) {
+ stopReportingCrashesLBp(r);
}
- if (res == AppErrorDialog.RESTART) {
+ }
+ if (res == AppErrorDialog.RESTART) {
+ synchronized (mService) {
mService.mProcessList.removeProcessLocked(r, false, true,
ApplicationExitInfo.REASON_CRASH, "crash");
- if (taskId != INVALID_TASK_ID) {
- try {
- mService.startActivityFromRecents(taskId,
- ActivityOptions.makeBasic().toBundle());
- } catch (IllegalArgumentException e) {
- // Hmm...that didn't work. Task should either be in recents or associated
- // with a stack.
- Slog.e(TAG, "Could not restart taskId=" + taskId, e);
- }
+ }
+ if (taskId != INVALID_TASK_ID) {
+ try {
+ mService.startActivityFromRecents(taskId,
+ ActivityOptions.makeBasic().toBundle());
+ } catch (IllegalArgumentException e) {
+ // Hmm...that didn't work. Task should either be in recents or associated
+ // with a stack.
+ Slog.e(TAG, "Could not restart taskId=" + taskId, e);
}
}
- if (res == AppErrorDialog.FORCE_QUIT) {
- final long orig = Binder.clearCallingIdentity();
- try {
- // Kill it with fire!
- mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
- if (!r.isPersistent()) {
+ }
+ if (res == AppErrorDialog.FORCE_QUIT) {
+ final long orig = Binder.clearCallingIdentity();
+ try {
+ // Kill it with fire!
+ mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
+ if (!r.isPersistent()) {
+ synchronized (mService) {
mService.mProcessList.removeProcessLocked(r, false, false,
ApplicationExitInfo.REASON_CRASH, "crash");
- mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
}
- } finally {
- Binder.restoreCallingIdentity(orig);
+ mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
}
+ } finally {
+ Binder.restoreCallingIdentity(orig);
}
- if (res == AppErrorDialog.APP_INFO) {
- appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
- appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- }
- if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
+ }
+ if (res == AppErrorDialog.APP_INFO) {
+ appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
+ appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+ if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
+ synchronized (mService) {
appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
}
}
@@ -769,7 +803,7 @@
return report;
}
- boolean handleAppCrashLocked(ProcessRecord app, String reason,
+ private boolean handleAppCrashLocked(ProcessRecord app, String reason,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
final long now = SystemClock.uptimeMillis();
final boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
@@ -783,109 +817,116 @@
Long crashTimePersistent;
boolean tryAgain = false;
- if (!app.isolated) {
- crashTime = mProcessCrashTimes.get(app.processName, app.uid);
- crashTimePersistent = mProcessCrashTimesPersistent.get(app.processName, app.uid);
- } else {
- crashTime = crashTimePersistent = null;
- }
-
- // Bump up the crash count of any services currently running in the proc.
- for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
- // Any services running in the application need to be placed
- // back in the pending list.
- ServiceRecord sr = app.getRunningServiceAt(i);
- // If the service was restarted a while ago, then reset crash count, else increment it.
- if (now > sr.restartTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
- sr.crashCount = 1;
+ synchronized (mBadProcessLock) {
+ if (!app.isolated) {
+ crashTime = mProcessCrashTimes.get(app.processName, app.uid);
+ crashTimePersistent = mProcessCrashTimesPersistent.get(app.processName, app.uid);
} else {
- sr.crashCount++;
+ crashTime = crashTimePersistent = null;
}
- // Allow restarting for started or bound foreground services that are crashing.
- // This includes wallpapers.
- if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
- && (sr.isForeground || procIsBoundForeground)) {
- tryAgain = true;
- }
- }
- final boolean quickCrash = crashTime != null
- && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
- if (quickCrash || isProcOverCrashLimit(app, now)) {
- // The process either crashed again very quickly or has been crashing periodically in
- // the last few hours. If it was a bound foreground service, let's try to restart again
- // in a while, otherwise the process loses!
- Slog.w(TAG, "Process " + app.processName + " has crashed too many times, killing!"
- + " Reason: " + (quickCrash ? "crashed quickly" : "over process crash limit"));
- EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
- app.userId, app.processName, app.uid);
- mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
- if (!app.isPersistent()) {
- // We don't want to start this process again until the user
- // explicitly does so... but for persistent process, we really
- // need to keep it running. If a persistent process is actually
- // repeatedly crashing, then badness for everyone.
- EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
- app.processName);
- if (!app.isolated) {
- // XXX We don't have a way to mark isolated processes
- // as bad, since they don't have a persistent identity.
- markBadProcess(app.processName, app.uid,
- new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
- mProcessCrashTimes.remove(app.processName, app.uid);
- mProcessCrashCounts.remove(app.processName, app.uid);
+ // Bump up the crash count of any services currently running in the proc.
+ for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
+ // Any services running in the application need to be placed
+ // back in the pending list.
+ ServiceRecord sr = app.getRunningServiceAt(i);
+ // If the service was restarted a while ago, then reset crash count,
+ // else increment it.
+ if (now > sr.restartTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
+ sr.crashCount = 1;
+ } else {
+ sr.crashCount++;
}
- app.bad = true;
- app.removed = true;
- // Don't let services in this process be restarted and potentially
- // annoy the user repeatedly. Unless it is persistent, since those
- // processes run critical code.
- mService.mProcessList.removeProcessLocked(app, false, tryAgain,
- ApplicationExitInfo.REASON_CRASH, "crash");
+ // Allow restarting for started or bound foreground services that are crashing.
+ // This includes wallpapers.
+ if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
+ && (sr.isForeground || procIsBoundForeground)) {
+ tryAgain = true;
+ }
+ }
+
+ final boolean quickCrash = crashTime != null
+ && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
+ if (quickCrash || isProcOverCrashLimitLBp(app, now)) {
+ // The process either crashed again very quickly or has been crashing periodically
+ // in the last few hours. If it was a bound foreground service, let's try to
+ // restart again in a while, otherwise the process loses!
+ Slog.w(TAG, "Process " + app.processName + " has crashed too many times, killing!"
+ + " Reason: "
+ + (quickCrash ? "crashed quickly" : "over process crash limit"));
+ EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
+ app.userId, app.processName, app.uid);
+ mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
+ if (!app.isPersistent()) {
+ // We don't want to start this process again until the user
+ // explicitly does so... but for persistent process, we really
+ // need to keep it running. If a persistent process is actually
+ // repeatedly crashing, then badness for everyone.
+ EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
+ app.processName);
+ if (!app.isolated) {
+ // XXX We don't have a way to mark isolated processes
+ // as bad, since they don't have a persistent identity.
+ markBadProcess(app.processName, app.uid,
+ new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
+ mProcessCrashTimes.remove(app.processName, app.uid);
+ mProcessCrashCounts.remove(app.processName, app.uid);
+ }
+ app.bad = true;
+ app.removed = true;
+ // Don't let services in this process be restarted and potentially
+ // annoy the user repeatedly. Unless it is persistent, since those
+ // processes run critical code.
+ mService.mProcessList.removeProcessLocked(app, false, tryAgain,
+ ApplicationExitInfo.REASON_CRASH, "crash");
+ mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
+ if (!showBackground) {
+ return false;
+ }
+ }
mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
- if (!showBackground) {
- return false;
+ } else {
+ final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
+ app.getWindowProcessController(), reason);
+ if (data != null) {
+ data.taskId = affectedTaskId;
+ }
+ if (data != null && crashTimePersistent != null
+ && now < crashTimePersistent
+ + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
+ data.repeating = true;
}
}
- mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
- } else {
- final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
- app.getWindowProcessController(), reason);
- if (data != null) {
- data.taskId = affectedTaskId;
+
+ if (data != null && tryAgain) {
+ data.isRestartableForService = true;
}
- if (data != null && crashTimePersistent != null
- && now < crashTimePersistent + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
- data.repeating = true;
+
+ // If the crashing process is what we consider to be the "home process" and it has been
+ // replaced by a third-party app, clear the package preferred activities from packages
+ // with a home activity running in the process to prevent a repeatedly crashing app
+ // from blocking the user to manually clear the list.
+ final WindowProcessController proc = app.getWindowProcessController();
+ if (proc.isHomeProcess() && proc.hasActivities()
+ && (app.info.flags & FLAG_SYSTEM) == 0) {
+ proc.clearPackagePreferredForHomeActivities();
}
- }
- if (data != null && tryAgain) {
- data.isRestartableForService = true;
- }
-
- // If the crashing process is what we consider to be the "home process" and it has been
- // replaced by a third-party app, clear the package preferred activities from packages
- // with a home activity running in the process to prevent a repeatedly crashing app
- // from blocking the user to manually clear the list.
- final WindowProcessController proc = app.getWindowProcessController();
- if (proc.isHomeProcess() && proc.hasActivities() && (app.info.flags & FLAG_SYSTEM) == 0) {
- proc.clearPackagePreferredForHomeActivities();
- }
-
- if (!app.isolated) {
- // XXX Can't keep track of crash times for isolated processes,
- // because they don't have a persistent identity.
- mProcessCrashTimes.put(app.processName, app.uid, now);
- mProcessCrashTimesPersistent.put(app.processName, app.uid, now);
- updateProcessCrashCount(app.processName, app.uid, now);
+ if (!app.isolated) {
+ // XXX Can't keep track of crash times for isolated processes,
+ // because they don't have a persistent identity.
+ mProcessCrashTimes.put(app.processName, app.uid, now);
+ mProcessCrashTimesPersistent.put(app.processName, app.uid, now);
+ updateProcessCrashCountLBp(app.processName, app.uid, now);
+ }
}
if (app.crashHandler != null) mService.mHandler.post(app.crashHandler);
return true;
}
- private void updateProcessCrashCount(String processName, int uid, long now) {
+ @GuardedBy("mBadProcessLock")
+ private void updateProcessCrashCountLBp(String processName, int uid, long now) {
Pair<Long, Integer> count = mProcessCrashCounts.get(processName, uid);
if (count == null || (count.first + PROCESS_CRASH_COUNT_RESET_INTERVAL) < now) {
count = new Pair<>(now, 1);
@@ -895,7 +936,8 @@
mProcessCrashCounts.put(processName, uid, count);
}
- private boolean isProcOverCrashLimit(ProcessRecord app, long now) {
+ @GuardedBy("mBadProcessLock")
+ private boolean isProcOverCrashLimitLBp(ProcessRecord app, long now) {
final Pair<Long, Integer> crashCount = mProcessCrashCounts.get(app.processName, app.uid);
return !app.isolated && crashCount != null
&& now < (crashCount.first + PROCESS_CRASH_COUNT_RESET_INTERVAL)
@@ -938,41 +980,44 @@
return;
}
Long crashShowErrorTime = null;
- if (!proc.isolated) {
- crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.processName,
- proc.uid);
- }
- final boolean showFirstCrash = Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0;
- final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
- 0,
- mService.mUserController.getCurrentUserId()) != 0;
- final boolean crashSilenced = mAppsNotReportingCrashes != null &&
- mAppsNotReportingCrashes.contains(proc.info.packageName);
- final long now = SystemClock.uptimeMillis();
- final boolean shouldThottle = crashShowErrorTime != null
- && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
- if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
- && !crashSilenced && !shouldThottle
- && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
- proc.getDialogController().showCrashDialogs(data);
+ synchronized (mBadProcessLock) {
if (!proc.isolated) {
- mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
+ crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.processName,
+ proc.uid);
}
- } else {
- // The device is asleep, so just pretend that the user
- // saw a crash dialog and hit "force quit".
- if (res != null) {
- res.set(AppErrorDialog.CANT_SHOW);
+ final boolean showFirstCrash = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0;
+ final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+ 0,
+ mService.mUserController.getCurrentUserId()) != 0;
+ final boolean crashSilenced = mAppsNotReportingCrashes != null
+ && mAppsNotReportingCrashes.contains(proc.info.packageName);
+ final long now = SystemClock.uptimeMillis();
+ final boolean shouldThottle = crashShowErrorTime != null
+ && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
+ if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
+ && !crashSilenced && !shouldThottle
+ && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
+ proc.getDialogController().showCrashDialogs(data);
+ if (!proc.isolated) {
+ mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
+ }
+ } else {
+ // The device is asleep, so just pretend that the user
+ // saw a crash dialog and hit "force quit".
+ if (res != null) {
+ res.set(AppErrorDialog.CANT_SHOW);
+ }
}
}
}
}
- private void stopReportingCrashesLocked(ProcessRecord proc) {
+ @GuardedBy("mBadProcessLock")
+ private void stopReportingCrashesLBp(ProcessRecord proc) {
if (mAppsNotReportingCrashes == null) {
mAppsNotReportingCrashes = new ArraySet<>();
}
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 20cad18..48aa8be 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -79,6 +79,7 @@
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -159,8 +160,7 @@
* persistent storage.
*/
@VisibleForTesting
- @GuardedBy("mLock")
- boolean mAppExitInfoLoaded = false;
+ AtomicBoolean mAppExitInfoLoaded = new AtomicBoolean();
/**
* Temporary list being used to filter/sort intermediate results in {@link #getExitInfo}.
@@ -273,51 +273,45 @@
return;
}
- synchronized (mLock) {
- if (!mAppExitInfoLoaded) {
- return;
- }
- mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecordLocked(app))
- .sendToTarget();
+ if (!mAppExitInfoLoaded.get()) {
+ return;
}
+ mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecord(app))
+ .sendToTarget();
}
void scheduleNoteAppKill(final ProcessRecord app, final @Reason int reason,
final @SubReason int subReason, final String msg) {
- synchronized (mLock) {
- if (!mAppExitInfoLoaded) {
- return;
- }
- if (app == null || app.info == null) {
- return;
- }
-
- ApplicationExitInfo raw = obtainRawRecordLocked(app);
- raw.setReason(reason);
- raw.setSubReason(subReason);
- raw.setDescription(msg);
- mKillHandler.obtainMessage(KillHandler.MSG_APP_KILL, raw).sendToTarget();
+ if (!mAppExitInfoLoaded.get()) {
+ return;
}
+ if (app == null || app.info == null) {
+ return;
+ }
+
+ ApplicationExitInfo raw = obtainRawRecord(app);
+ raw.setReason(reason);
+ raw.setSubReason(subReason);
+ raw.setDescription(msg);
+ mKillHandler.obtainMessage(KillHandler.MSG_APP_KILL, raw).sendToTarget();
}
void scheduleNoteAppKill(final int pid, final int uid, final @Reason int reason,
final @SubReason int subReason, final String msg) {
- synchronized (mLock) {
- if (!mAppExitInfoLoaded) {
- return;
+ if (!mAppExitInfoLoaded.get()) {
+ return;
+ }
+ ProcessRecord app;
+ synchronized (mService.mPidsSelfLocked) {
+ app = mService.mPidsSelfLocked.get(pid);
+ }
+ if (app == null) {
+ if (DEBUG_PROCESSES) {
+ Slog.w(TAG, "Skipping saving the kill reason for pid " + pid
+ + "(uid=" + uid + ") since its process record is not found");
}
- ProcessRecord app;
- synchronized (mService.mPidsSelfLocked) {
- app = mService.mPidsSelfLocked.get(pid);
- }
- if (app == null) {
- if (DEBUG_PROCESSES) {
- Slog.w(TAG, "Skipping saving the kill reason for pid " + pid
- + "(uid=" + uid + ") since its process record is not found");
- }
- } else {
- scheduleNoteAppKill(app, reason, subReason, msg);
- }
+ } else {
+ scheduleNoteAppKill(app, reason, subReason, msg);
}
}
@@ -414,7 +408,7 @@
@GuardedBy("mLock")
private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw) {
- if (!mAppExitInfoLoaded) {
+ if (!mAppExitInfoLoaded.get()) {
Slog.w(TAG, "Skipping saving the exit info due to ongoing loading from storage");
return null;
}
@@ -627,9 +621,7 @@
@VisibleForTesting
void loadExistingProcessExitInfo() {
if (!mProcExitInfoFile.canRead()) {
- synchronized (mLock) {
- mAppExitInfoLoaded = true;
- }
+ mAppExitInfoLoaded.set(true);
return;
}
@@ -665,7 +657,7 @@
}
synchronized (mLock) {
pruneAnrTracesIfNecessaryLocked();
- mAppExitInfoLoaded = true;
+ mAppExitInfoLoaded.set(true);
}
}
@@ -834,7 +826,7 @@
container.addExitInfoLocked(info);
}
- @GuardedBy("mLocked")
+ @GuardedBy("mLock")
private void forEachPackageLocked(
BiFunction<String, SparseArray<AppExitInfoContainer>, Integer> callback) {
if (callback != null) {
@@ -859,7 +851,7 @@
}
}
- @GuardedBy("mLocked")
+ @GuardedBy("mLock")
private void removePackageLocked(String packageName, int uid, boolean removeUid, int userId) {
if (removeUid) {
mActiveAppStateSummary.remove(uid);
@@ -896,7 +888,7 @@
}
}
- @GuardedBy("mLocked")
+ @GuardedBy("mLock")
private void removeByUserIdLocked(final int userId) {
if (userId == UserHandle.USER_ALL) {
mData.getMap().clear();
@@ -922,35 +914,36 @@
}
@VisibleForTesting
- @GuardedBy("mLock")
- ApplicationExitInfo obtainRawRecordLocked(ProcessRecord app) {
+ ApplicationExitInfo obtainRawRecord(ProcessRecord app) {
ApplicationExitInfo info = mRawRecordsPool.acquire();
if (info == null) {
info = new ApplicationExitInfo();
}
- final int definingUid = app.hostingRecord != null ? app.hostingRecord.getDefiningUid() : 0;
- info.setPid(app.pid);
- info.setRealUid(app.uid);
- info.setPackageUid(app.info.uid);
- info.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid);
- info.setProcessName(app.processName);
- info.setConnectionGroup(app.connectionGroup);
- info.setPackageName(app.info.packageName);
- info.setPackageList(app.getPackageList());
- info.setReason(ApplicationExitInfo.REASON_UNKNOWN);
- info.setStatus(0);
- info.setImportance(procStateToImportance(app.setProcState));
- info.setPss(app.lastPss);
- info.setRss(app.mLastRss);
- info.setTimestamp(System.currentTimeMillis());
+ synchronized (mService) {
+ final int definingUid = app.hostingRecord != null
+ ? app.hostingRecord.getDefiningUid() : 0;
+ info.setPid(app.pid);
+ info.setRealUid(app.uid);
+ info.setPackageUid(app.info.uid);
+ info.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid);
+ info.setProcessName(app.processName);
+ info.setConnectionGroup(app.connectionGroup);
+ info.setPackageName(app.info.packageName);
+ info.setPackageList(app.getPackageList());
+ info.setReason(ApplicationExitInfo.REASON_UNKNOWN);
+ info.setStatus(0);
+ info.setImportance(procStateToImportance(app.setProcState));
+ info.setPss(app.mProfile.getLastPss());
+ info.setRss(app.mProfile.getLastRss());
+ info.setTimestamp(System.currentTimeMillis());
+ }
return info;
}
@VisibleForTesting
- @GuardedBy("mLock")
- void recycleRawRecordLocked(ApplicationExitInfo info) {
+ void recycleRawRecord(ApplicationExitInfo info) {
info.setProcessName(null);
info.setDescription(null);
info.setPackageList(null);
@@ -1549,16 +1542,16 @@
ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj;
synchronized (mLock) {
handleNoteProcessDiedLocked(raw);
- recycleRawRecordLocked(raw);
}
+ recycleRawRecord(raw);
}
break;
case MSG_APP_KILL: {
ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj;
synchronized (mLock) {
handleNoteAppKillLocked(raw);
- recycleRawRecordLocked(raw);
}
+ recycleRawRecord(raw);
}
break;
default:
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index dac5325..96e6f6e 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -63,8 +63,8 @@
? data.aInfo.loadLabel(context.getPackageManager())
: null;
CharSequence name2 = null;
- if ((mProc.pkgList.size() == 1) &&
- (name2=context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
+ if ((mProc.getPkgList().size() == 1)
+ && (name2 = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
if (name1 != null) {
resid = com.android.internal.R.string.anr_activity_application;
} else {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index cace260..3df058c 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -29,8 +29,22 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.DUMP_MEM_OOM_ADJ;
+import static com.android.server.am.ActivityManagerService.DUMP_MEM_OOM_LABEL;
+import static com.android.server.am.ActivityManagerService.GC_BACKGROUND_PROCESSES_MSG;
+import static com.android.server.am.ActivityManagerService.KSM_SHARED;
+import static com.android.server.am.ActivityManagerService.KSM_SHARING;
+import static com.android.server.am.ActivityManagerService.KSM_UNSHARED;
+import static com.android.server.am.ActivityManagerService.KSM_VOLATILE;
+import static com.android.server.am.ActivityManagerService.REPORT_MEM_USAGE_MSG;
+import static com.android.server.am.ActivityManagerService.appendBasicMemEntry;
+import static com.android.server.am.ActivityManagerService.appendMemBucket;
+import static com.android.server.am.ActivityManagerService.appendMemInfo;
+import static com.android.server.am.ActivityManagerService.getKsmInfo;
+import static com.android.server.am.ActivityManagerService.stringifyKBSize;
import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD;
import android.annotation.BroadcastBehavior;
import android.annotation.NonNull;
@@ -75,16 +89,19 @@
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
import com.android.server.am.LowMemDetector.MemFactor;
-import com.android.server.am.ProcessList.ProcStateMemTracker;
import com.android.server.utils.PriorityDump;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -162,8 +179,8 @@
/**
* Processes we want to collect PSS data from.
*/
- @GuardedBy("mService")
- private final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
+ @GuardedBy("mProfilerLock")
+ private final ArrayList<ProcessProfileRecord> mPendingPssProfiles = new ArrayList<>();
/**
* Depth of overlapping activity-start PSS deferral notes
@@ -173,14 +190,14 @@
/**
* Last time we requested PSS data of all processes.
*/
- @GuardedBy("mService")
+ @GuardedBy("mProfilerLock")
private long mLastFullPssTime = SystemClock.uptimeMillis();
/**
* If set, the next time we collect PSS data we should do a full collection
* with data from native processes and the kernel.
*/
- @GuardedBy("mService")
+ @GuardedBy("mProfilerLock")
private boolean mFullPssPending = false;
/**
@@ -188,8 +205,7 @@
* much more rapidly to try to collect better data when the tests are rapidly
* running through apps.
*/
- @GuardedBy("mService")
- private boolean mTestPssMode = false;
+ private volatile boolean mTestPssMode = false;
@GuardedBy("mService")
private final LowMemDetector mLowMemDetector;
@@ -233,21 +249,53 @@
private long mLowRamStartTime = 0;
/**
+ * Last time we report a memory usage.
+ */
+ @GuardedBy("mService")
+ private long mLastMemUsageReportTime = 0;
+
+ /**
+ * List of processes that should gc as soon as things are idle.
+ */
+ @GuardedBy("mProfilerLock")
+ private final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<>();
+
+ /**
* Stores a map of process name -> agent string. When a process is started and mAgentAppMap
* is not null, this map is checked and the mapped agent installed during bind-time. Note:
* A non-null agent in mProfileInfo overrides this.
*/
+ @GuardedBy("mProfilerLock")
private @Nullable Map<String, String> mAppAgentMap = null;
+ @GuardedBy("mProfilerLock")
private int mProfileType = 0;
+
+ @GuardedBy("mProfilerLock")
+ private final ProfileData mProfileData = new ProfileData();
+
+ @GuardedBy("mProfilerLock")
private final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
+
+ @GuardedBy("mProfilerLock")
private String mMemWatchDumpProcName;
+
+ @GuardedBy("mProfilerLock")
private Uri mMemWatchDumpUri;
+
+ @GuardedBy("mProfilerLock")
private int mMemWatchDumpPid;
+
+ @GuardedBy("mProfilerLock")
private int mMemWatchDumpUid;
+
+ @GuardedBy("mProfilerLock")
private boolean mMemWatchIsUserInitiated;
+ @GuardedBy("mService")
boolean mHasHomeProcess;
+
+ @GuardedBy("mService")
boolean mHasPreviousProcess;
/**
@@ -263,8 +311,7 @@
private final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true);
private final CountDownLatch mProcessCpuInitLatch = new CountDownLatch(1);
- private long mLastWriteTime = 0;
- private final ProfileData mProfileData = new ProfileData();
+ private volatile long mLastWriteTime = 0;
/**
* Runtime CPU use collection thread. This object's lock is used to
@@ -276,6 +323,15 @@
private final Handler mBgHandler;
/**
+ * The lock to guard some of the profiling data here and {@link ProcessProfileRecord}.
+ *
+ * <p>
+ * The function suffix with this lock would be "-LPf" (Locked with Profiler lock).
+ * </p>
+ */
+ final Object mProfilerLock = new Object();
+
+ /**
* Observe DeviceConfig changes to the PSS calculation interval
*/
private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener =
@@ -359,7 +415,7 @@
private void collectPssInBackground() {
long start = SystemClock.uptimeMillis();
MemInfoReader memInfo = null;
- synchronized (mService) {
+ synchronized (mProfilerLock) {
if (mFullPssPending) {
mFullPssPending = false;
memInfo = new MemInfoReader();
@@ -404,65 +460,67 @@
int num = 0;
long[] tmp = new long[3];
do {
- ProcessRecord proc;
+ ProcessProfileRecord profile;
int procState;
int statType;
int pid = -1;
long lastPssTime;
- synchronized (mService) {
- if (mPendingPssProcesses.size() <= 0) {
+ synchronized (mProfilerLock) {
+ if (mPendingPssProfiles.size() <= 0) {
if (mTestPssMode || DEBUG_PSS) {
Slog.d(TAG_PSS,
"Collected pss of " + num + " processes in "
+ (SystemClock.uptimeMillis() - start) + "ms");
}
- mPendingPssProcesses.clear();
+ mPendingPssProfiles.clear();
return;
}
- proc = mPendingPssProcesses.remove(0);
- procState = proc.pssProcState;
- statType = proc.pssStatType;
- lastPssTime = proc.lastPssTime;
+ profile = mPendingPssProfiles.remove(0);
+ procState = profile.getPssProcState();
+ statType = profile.getPssStatType();
+ lastPssTime = profile.getLastPssTime();
long now = SystemClock.uptimeMillis();
- if (proc.thread != null && procState == proc.setProcState
+ if (profile.getThread() != null && procState == profile.getSetProcState()
&& (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE) < now) {
- pid = proc.pid;
+ pid = profile.getPid();
} else {
- abortNextPssTime(proc.procStateMemTracker);
+ profile.abortNextPssTime();
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Skipped pss collection of " + pid
+ ": still need "
+ (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE - now)
+ "ms until safe");
}
- proc = null;
+ profile = null;
pid = 0;
}
}
- if (proc != null) {
+ if (profile != null) {
long startTime = SystemClock.currentThreadTimeMillis();
// skip background PSS calculation of apps that are capturing
// camera imagery
- final boolean usingCamera = mService.isCameraActiveForUid(proc.uid);
+ final boolean usingCamera = mService.isCameraActiveForUid(profile.mApp.uid);
long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
long endTime = SystemClock.currentThreadTimeMillis();
- synchronized (mService) {
- if (pss != 0 && proc.thread != null && proc.setProcState == procState
- && proc.pid == pid && proc.lastPssTime == lastPssTime) {
+ synchronized (mProfilerLock) {
+ if (pss != 0 && profile.getThread() != null
+ && profile.getSetProcState() == procState
+ && profile.getPid() == pid && profile.getLastPssTime() == lastPssTime) {
num++;
- commitNextPssTime(proc.procStateMemTracker);
- recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], tmp[2],
+ profile.commitNextPssTime();
+ recordPssSampleLPf(profile, procState, pss, tmp[0], tmp[1], tmp[2],
statType, endTime - startTime, SystemClock.uptimeMillis());
} else {
- abortNextPssTime(proc.procStateMemTracker);
+ profile.abortNextPssTime();
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Skipped pss collection of " + pid
- + ": " + (proc.thread == null ? "NO_THREAD " : "")
+ + ": " + (profile.getThread() == null ? "NO_THREAD " : "")
+ (usingCamera ? "CAMERA " : "")
- + (proc.pid != pid ? "PID_CHANGED " : "")
+ + (profile.getPid() != pid ? "PID_CHANGED " : "")
+ " initState=" + procState + " curState="
- + proc.setProcState + " "
- + (proc.lastPssTime != lastPssTime ? "TIME_CHANGED" : ""));
+ + profile.getSetProcState() + " "
+ + (profile.getLastPssTime() != lastPssTime
+ ? "TIME_CHANGED" : ""));
}
}
}
@@ -470,73 +528,61 @@
} while (true);
}
- private static void commitNextPssTime(ProcStateMemTracker tracker) {
- if (tracker.mPendingMemState >= 0) {
- tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
- tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
- tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
- tracker.mPendingMemState = -1;
- }
- }
-
- private static void abortNextPssTime(ProcStateMemTracker tracker) {
- tracker.mPendingMemState = -1;
- }
-
- @GuardedBy("mService")
- void updateNextPssTimeLocked(int procState, ProcessRecord app, long now, boolean forceUpdate) {
+ @GuardedBy("mProfilerLock")
+ void updateNextPssTimeLPf(int procState, ProcessProfileRecord profile, long now,
+ boolean forceUpdate) {
if (!forceUpdate) {
- if (now <= app.nextPssTime
- && now <= Math.max(app.lastPssTime + ProcessList.PSS_MAX_INTERVAL,
- app.lastStateTime + ProcessList.minTimeFromStateChange(mTestPssMode))) {
+ if (now <= profile.getNextPssTime() && now <= Math.max(profile.getLastPssTime()
+ + ProcessList.PSS_MAX_INTERVAL, profile.getLastStateTime()
+ + ProcessList.minTimeFromStateChange(mTestPssMode))) {
// update is not due, ignore it.
return;
}
- if (!requestPssLocked(app, app.setProcState)) {
+ if (!requestPssLPf(profile, procState)) {
return;
}
}
- app.nextPssTime = ProcessList.computeNextPssTime(procState, app.procStateMemTracker,
- mTestPssMode, mService.mAtmInternal.isSleeping(), now);
+ profile.setNextPssTime(profile.computeNextPssTime(procState,
+ mTestPssMode, mService.mAtmInternal.isSleeping(), now));
}
/**
* Record new PSS sample for a process.
*/
- @GuardedBy("mService")
- private void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss,
+ @GuardedBy("mProfilerLock")
+ private void recordPssSampleLPf(ProcessProfileRecord profile, int procState, long pss, long uss,
long swapPss, long rss, int statType, long pssDuration, long now) {
- EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
+ final ProcessRecord proc = profile.mApp;
+ EventLogTags.writeAmPss(
+ profile.getPid(), proc.uid, proc.processName, pss * 1024, uss * 1024,
swapPss * 1024, rss * 1024, statType, procState, pssDuration);
- proc.lastPssTime = now;
- synchronized (mService.mProcessStats.mLock) {
- proc.baseProcessTracker.addPss(
- pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
- }
- for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ profile.setLastPssTime(now);
+ profile.addPss(pss, uss, rss, true, statType, pssDuration);
+ proc.getPkgList().forEachPackageProcessStats(holder -> {
FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
proc.info.uid,
holder.state.getName(),
holder.state.getPackage(),
- pss, uss, rss, statType, pssDuration,
+ pss, uss, rss,
+ statType, pssDuration,
holder.appVersion);
- }
+ });
if (DEBUG_PSS) {
Slog.d(TAG_PSS,
- "pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
+ "pss of " + proc.toShortString() + ": " + pss
+ + " lastPss=" + profile.getLastPss()
+ " state=" + ProcessList.makeProcStateString(procState));
}
- if (proc.initialIdlePss == 0) {
- proc.initialIdlePss = pss;
+ if (profile.getInitialIdlePss() == 0) {
+ profile.setInitialIdlePss(pss);
}
- proc.lastPss = pss;
- proc.lastSwapPss = swapPss;
+ profile.setLastPss(pss);
+ profile.setLastSwapPss(swapPss);
if (procState >= ActivityManager.PROCESS_STATE_HOME) {
- proc.lastCachedPss = pss;
- proc.lastCachedSwapPss = swapPss;
+ profile.setLastCachedPss(pss);
+ profile.setLastCachedSwapPss(swapPss);
}
- proc.mLastRss = rss;
+ profile.setLastRss(rss);
final SparseArray<Pair<Long, String>> watchUids =
mMemWatchProcesses.getMap().get(proc.processName);
@@ -551,7 +597,8 @@
}
}
if (check != null) {
- if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
+ if ((pss * 1024) >= check && profile.getThread() != null
+ && mMemWatchDumpProcName == null) {
boolean isDebuggable = Build.IS_DEBUGGABLE;
if (!isDebuggable) {
if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
@@ -560,7 +607,7 @@
}
if (isDebuggable) {
Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + "; reporting");
- startHeapDumpLocked(proc, false);
+ startHeapDumpLPf(profile, false);
} else {
Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check
+ ", but debugging not enabled");
@@ -570,12 +617,13 @@
}
private final class RecordPssRunnable implements Runnable {
- private final ProcessRecord mProc;
+ private final ProcessProfileRecord mProfile;
private final Uri mDumpUri;
private final ContentResolver mContentResolver;
- RecordPssRunnable(ProcessRecord proc, Uri dumpUri, ContentResolver contentResolver) {
- mProc = proc;
+ RecordPssRunnable(ProcessProfileRecord profile, Uri dumpUri,
+ ContentResolver contentResolver) {
+ mProfile = profile;
mDumpUri = dumpUri;
mContentResolver = contentResolver;
}
@@ -583,12 +631,12 @@
@Override
public void run() {
try (ParcelFileDescriptor fd = mContentResolver.openFileDescriptor(mDumpUri, "rw")) {
- IApplicationThread thread = mProc.thread;
+ IApplicationThread thread = mProfile.getThread();
if (thread != null) {
try {
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Requesting dump heap from "
- + mProc + " to " + mDumpUri.getPath());
+ + mProfile.mApp + " to " + mDumpUri.getPath());
}
thread.dumpHeap(/* managed= */ true,
/* mallocInfo= */ false, /* runGc= */ false,
@@ -601,16 +649,17 @@
Slog.e(TAG, "Failed to dump heap", e);
// Need to clear the heap dump variables, otherwise no further heap dumps will be
// attempted.
- abortHeapDump(mProc.processName);
+ abortHeapDump(mProfile.mApp.processName);
}
}
}
- @GuardedBy("mService")
- void startHeapDumpLocked(ProcessRecord proc, boolean isUserInitiated) {
+ @GuardedBy("mProfilerLock")
+ void startHeapDumpLPf(ProcessProfileRecord profile, boolean isUserInitiated) {
+ final ProcessRecord proc = profile.mApp;
mMemWatchDumpProcName = proc.processName;
mMemWatchDumpUri = makeHeapDumpUri(proc.processName);
- mMemWatchDumpPid = proc.pid;
+ mMemWatchDumpPid = profile.getPid();
mMemWatchDumpUid = proc.uid;
mMemWatchIsUserInitiated = isUserInitiated;
Context ctx;
@@ -621,11 +670,11 @@
throw new RuntimeException("android package not found.");
}
BackgroundThread.getHandler().post(
- new RecordPssRunnable(proc, mMemWatchDumpUri, ctx.getContentResolver()));
+ new RecordPssRunnable(profile, mMemWatchDumpUri, ctx.getContentResolver()));
}
void dumpHeapFinished(String path, int callerPid) {
- synchronized (mService) {
+ synchronized (mProfilerLock) {
if (callerPid != mMemWatchDumpPid) {
Slog.w(TAG, "dumpHeapFinished: Calling pid " + Binder.getCallingPid()
+ " does not match last pid " + mMemWatchDumpPid);
@@ -651,7 +700,7 @@
final long memLimit;
final String reportPackage;
final boolean isUserInitiated;
- synchronized (mService) {
+ synchronized (mProfilerLock) {
uid = mMemWatchDumpUid;
procName = mMemWatchDumpProcName;
Pair<Long, String> val = mMemWatchProcesses.get(procName, uid);
@@ -695,7 +744,7 @@
void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
String reportPackage) {
- synchronized (mService) {
+ synchronized (mProfilerLock) {
if (maxMemSize > 0) {
mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage));
} else {
@@ -717,7 +766,7 @@
void handleAbortDumpHeap(String procName) {
if (procName != null) {
- synchronized (mService) {
+ synchronized (mProfilerLock) {
if (procName.equals(mMemWatchDumpProcName)) {
mMemWatchDumpProcName = null;
mMemWatchDumpUri = null;
@@ -736,24 +785,24 @@
/**
* Schedule PSS collection of a process.
*/
- @GuardedBy("mService")
- private boolean requestPssLocked(ProcessRecord proc, int procState) {
- if (mPendingPssProcesses.contains(proc)) {
+ @GuardedBy("mProfilerLock")
+ private boolean requestPssLPf(ProcessProfileRecord profile, int procState) {
+ if (mPendingPssProfiles.contains(profile)) {
return false;
}
- if (mPendingPssProcesses.size() == 0) {
+ if (mPendingPssProfiles.size() == 0) {
final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
? mPssDeferralTime : 0;
if (DEBUG_PSS && deferral > 0) {
- Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by "
+ Slog.d(TAG_PSS, "requestPssLPf() deferring PSS request by "
+ deferral + " ms");
}
mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, deferral);
}
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc);
- proc.pssProcState = procState;
- proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE;
- mPendingPssProcesses.add(proc);
+ if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + profile.mApp);
+ profile.setPssProcState(procState);
+ profile.setPssStatType(ProcessStats.ADD_PSS_INTERNAL_SINGLE);
+ mPendingPssProfiles.add(profile);
return true;
}
@@ -761,9 +810,9 @@
* Re-defer a posted PSS collection pass, if one exists. Assumes deferral is
* currently active policy when called.
*/
- @GuardedBy("mService")
- private void deferPssIfNeededLocked() {
- if (mPendingPssProcesses.size() > 0) {
+ @GuardedBy("mProfilerLock")
+ private void deferPssIfNeededLPf() {
+ if (mPendingPssProfiles.size() > 0) {
mBgHandler.removeMessages(BgHandler.COLLECT_PSS_BG_MSG);
mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, mPssDeferralTime);
}
@@ -774,7 +823,9 @@
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Deferring PSS collection for activity start");
}
- deferPssIfNeededLocked();
+ synchronized (mProfilerLock) {
+ deferPssIfNeededLPf();
+ }
mActivityStartingNesting.getAndIncrement();
mBgHandler.sendEmptyMessageDelayed(BgHandler.STOP_DEFERRING_PSS_MSG, mPssDeferralTime);
}
@@ -803,40 +854,45 @@
*/
@GuardedBy("mService")
void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
- if (!always) {
- if (now < (mLastFullPssTime
- + (memLowered ? mService.mConstants.FULL_PSS_LOWERED_INTERVAL
- : mService.mConstants.FULL_PSS_MIN_INTERVAL))) {
- return;
+ synchronized (mProfilerLock) {
+ if (!always) {
+ if (now < (mLastFullPssTime
+ + (memLowered ? mService.mConstants.FULL_PSS_LOWERED_INTERVAL
+ : mService.mConstants.FULL_PSS_MIN_INTERVAL))) {
+ return;
+ }
}
- }
- if (DEBUG_PSS) {
- Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered);
- }
- mLastFullPssTime = now;
- mFullPssPending = true;
- for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
- abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);
- }
- mPendingPssProcesses.ensureCapacity(mService.mProcessList.getLruSizeLocked());
- mPendingPssProcesses.clear();
- for (int i = mService.mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
- ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
- if (app.thread == null || app.getCurProcState() == PROCESS_STATE_NONEXISTENT) {
- continue;
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered);
}
- if (memLowered || (always && now
- > app.lastStateTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
- || now > (app.lastStateTime + ProcessList.PSS_ALL_INTERVAL)) {
- app.pssProcState = app.setProcState;
- app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
- : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
- updateNextPssTimeLocked(app.getCurProcState(), app, now, true);
- mPendingPssProcesses.add(app);
+ mLastFullPssTime = now;
+ mFullPssPending = true;
+ for (int i = mPendingPssProfiles.size() - 1; i >= 0; i--) {
+ mPendingPssProfiles.get(i).abortNextPssTime();
}
- }
- if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) {
- mBgHandler.sendEmptyMessage(BgHandler.COLLECT_PSS_BG_MSG);
+ mPendingPssProfiles.ensureCapacity(mService.mProcessList.getLruSizeLocked());
+ mPendingPssProfiles.clear();
+ for (int i = mService.mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
+ ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ final ProcessProfileRecord profile = app.mProfile;
+ if (profile.getThread() == null
+ || profile.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
+ return;
+ }
+ final long lastStateTime = profile.getLastStateTime();
+ if (memLowered || (always
+ && now > lastStateTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
+ || now > (lastStateTime + ProcessList.PSS_ALL_INTERVAL)) {
+ profile.setPssProcState(profile.getSetProcState());
+ profile.setPssStatType(always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
+ : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM);
+ updateNextPssTimeLPf(profile.getSetProcState(), profile, now, true);
+ mPendingPssProfiles.add(profile);
+ }
+ }
+ if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) {
+ mBgHandler.sendEmptyMessage(BgHandler.COLLECT_PSS_BG_MSG);
+ }
}
}
@@ -851,8 +907,7 @@
}
}
- @GuardedBy("mService")
- boolean getTestPssModeLocked() {
+ boolean getTestPssMode() {
return mTestPssMode;
}
@@ -969,14 +1024,16 @@
if (factor < minFactor) factor = minFactor;
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
for (int i = 0; i < numOfLru; i++) {
- ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ final ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ final ProcessProfileRecord profile = app.mProfile;
+ final int trimMemoryLevel = profile.getTrimMemoryLevel();
if (allChanged || app.procStateChanged) {
mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
}
if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
&& !app.killedByAm) {
- if (app.trimMemoryLevel < curLevel && app.thread != null) {
+ if (trimMemoryLevel < curLevel && app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ,
@@ -987,7 +1044,7 @@
} catch (RemoteException e) {
}
}
- app.trimMemoryLevel = curLevel;
+ profile.setTrimMemoryLevel(curLevel);
step++;
if (step >= factor) {
step = 0;
@@ -1002,7 +1059,7 @@
}
} else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
&& !app.killedByAm) {
- if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
+ if (trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
&& app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
@@ -1015,15 +1072,15 @@
} catch (RemoteException e) {
}
}
- app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+ profile.setTrimMemoryLevel(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
} else {
if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && app.hasPendingUiClean()) {
+ || app.systemNoUi) && profile.hasPendingUiClean()) {
// If this application is now in the background and it
// had done UI, then give it the special trim level to
// have it free UI resources.
final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
- if (app.trimMemoryLevel < level && app.thread != null) {
+ if (trimMemoryLevel < level && app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
@@ -1033,9 +1090,9 @@
} catch (RemoteException e) {
}
}
- app.setPendingUiClean(false);
+ profile.setPendingUiClean(false);
}
- if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
+ if (trimMemoryLevel < fgTrimLevel && app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName
@@ -1045,7 +1102,7 @@
} catch (RemoteException e) {
}
}
- app.trimMemoryLevel = fgTrimLevel;
+ profile.setTrimMemoryLevel(fgTrimLevel);
}
}
} else {
@@ -1054,14 +1111,15 @@
mLowRamStartTime = 0;
}
for (int i = 0; i < numOfLru; i++) {
- ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ final ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ final ProcessProfileRecord profile = app.mProfile;
if (allChanged || app.procStateChanged) {
mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
}
if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && app.hasPendingUiClean()) {
- if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+ || app.systemNoUi) && profile.hasPendingUiClean()) {
+ if (profile.getTrimMemoryLevel() < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
&& app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
@@ -1074,9 +1132,9 @@
} catch (RemoteException e) {
}
}
- app.setPendingUiClean(false);
+ profile.setPendingUiClean(false);
}
- app.trimMemoryLevel = 0;
+ profile.setTrimMemoryLevel(0);
}
}
return allChanged;
@@ -1087,25 +1145,459 @@
return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now - mLowRamStartTime) : 0);
}
+ /**
+ * Ask a given process to GC right now.
+ */
+ @GuardedBy("mProfilerLock")
+ private void performAppGcLPf(ProcessRecord app) {
+ try {
+ final ProcessProfileRecord profile = app.mProfile;
+ profile.setLastRequestedGc(SystemClock.uptimeMillis());
+ IApplicationThread thread = profile.getThread();
+ if (thread != null) {
+ if (profile.getReportLowMemory()) {
+ profile.setReportLowMemory(false);
+ thread.scheduleLowMemory();
+ } else {
+ thread.processInBackground();
+ }
+ }
+ } catch (Exception e) {
+ // whatever.
+ }
+ }
+
+ /**
+ * Perform GCs on all processes that are waiting for it, but only
+ * if things are idle.
+ */
+ @GuardedBy("mProfilerLock")
+ private void performAppGcsLPf() {
+ if (mProcessesToGc.size() <= 0) {
+ return;
+ }
+ while (mProcessesToGc.size() > 0) {
+ final ProcessRecord proc = mProcessesToGc.remove(0);
+ final ProcessProfileRecord profile = proc.mProfile;
+ if (profile.getCurRawAdj() > ProcessList.PERCEPTIBLE_APP_ADJ
+ || profile.getReportLowMemory()) {
+ if ((profile.getLastRequestedGc() + mService.mConstants.GC_MIN_INTERVAL)
+ <= SystemClock.uptimeMillis()) {
+ // To avoid spamming the system, we will GC processes one
+ // at a time, waiting a few seconds between each.
+ performAppGcLPf(proc);
+ scheduleAppGcsLPf();
+ return;
+ } else {
+ // It hasn't been long enough since we last GCed this
+ // process... put it in the list to wait for its time.
+ addProcessToGcListLPf(proc);
+ break;
+ }
+ }
+ }
+
+ scheduleAppGcsLPf();
+ }
+
+ /**
+ * If all looks good, perform GCs on all processes waiting for them.
+ */
@GuardedBy("mService")
- private void stopProfilerLocked(ProcessRecord proc, int profileType) {
+ final void performAppGcsIfAppropriateLocked() {
+ synchronized (mProfilerLock) {
+ if (mService.canGcNowLocked()) {
+ performAppGcsLPf();
+ return;
+ }
+ // Still not idle, wait some more.
+ scheduleAppGcsLPf();
+ }
+ }
+
+ /**
+ * Schedule the execution of all pending app GCs.
+ */
+ @GuardedBy("mProfilerLock")
+ final void scheduleAppGcsLPf() {
+ mService.mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG);
+
+ if (mProcessesToGc.size() > 0) {
+ // Schedule a GC for the time to the next process.
+ ProcessRecord proc = mProcessesToGc.get(0);
+ Message msg = mService.mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);
+
+ long when = proc.mProfile.getLastRequestedGc() + mService.mConstants.GC_MIN_INTERVAL;
+ long now = SystemClock.uptimeMillis();
+ if (when < (now + mService.mConstants.GC_TIMEOUT)) {
+ when = now + mService.mConstants.GC_TIMEOUT;
+ }
+ mService.mHandler.sendMessageAtTime(msg, when);
+ }
+ }
+
+ /**
+ * Add a process to the array of processes waiting to be GCed. Keeps the
+ * list in sorted order by the last GC time. The process can't already be
+ * on the list.
+ */
+ @GuardedBy("mProfilerLock")
+ private void addProcessToGcListLPf(ProcessRecord proc) {
+ boolean added = false;
+ for (int i = mProcessesToGc.size() - 1; i >= 0; i--) {
+ if (mProcessesToGc.get(i).mProfile.getLastRequestedGc()
+ < proc.mProfile.getLastRequestedGc()) {
+ added = true;
+ mProcessesToGc.add(i + 1, proc);
+ break;
+ }
+ }
+ if (!added) {
+ mProcessesToGc.add(0, proc);
+ }
+ }
+
+ @GuardedBy("mService")
+ final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) {
+ // If there are no longer any background processes running,
+ // and the app that died was not running instrumentation,
+ // then tell everyone we are now low on memory.
+ if (!mService.mProcessList.haveBackgroundProcessLocked()) {
+ boolean doReport = Build.IS_DEBUGGABLE;
+ final long now = SystemClock.uptimeMillis();
+ if (doReport) {
+ if (now < (mLastMemUsageReportTime + 5 * 60 * 1000)) {
+ doReport = false;
+ } else {
+ mLastMemUsageReportTime = now;
+ }
+ }
+ final int lruSize = mService.mProcessList.getLruSizeLocked();
+ final ArrayList<ProcessMemInfo> memInfos = doReport
+ ? new ArrayList<ProcessMemInfo>(lruSize) : null;
+ EventLogTags.writeAmLowMemory(lruSize);
+ for (int i = lruSize - 1; i >= 0; i--) {
+ ProcessRecord rec = mService.mProcessList.mLruProcesses.get(i);
+ if (rec == dyingProc || rec.thread == null) {
+ return;
+ }
+ if (memInfos != null) {
+ memInfos.add(new ProcessMemInfo(rec.processName, rec.pid,
+ rec.setAdj, rec.setProcState,
+ rec.adjType, rec.makeAdjReason()));
+ }
+ final ProcessProfileRecord profile = rec.mProfile;
+ if ((profile.getLastLowMemory() + mService.mConstants.GC_MIN_INTERVAL) <= now) {
+ // The low memory report is overriding any current
+ // state for a GC request. Make sure to do
+ // heavy/important/visible/foreground processes first.
+ synchronized (mProfilerLock) {
+ if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+ profile.setLastRequestedGc(0);
+ } else {
+ profile.setLastRequestedGc(profile.getLastLowMemory());
+ }
+ profile.setReportLowMemory(true);
+ profile.setLastLowMemory(now);
+ mProcessesToGc.remove(rec);
+ addProcessToGcListLPf(rec);
+ }
+ }
+ }
+ if (doReport) {
+ Message msg = mService.mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos);
+ mService.mHandler.sendMessage(msg);
+ }
+ }
+ synchronized (mProfilerLock) {
+ scheduleAppGcsLPf();
+ }
+ }
+
+ void reportMemUsage(ArrayList<ProcessMemInfo> memInfos) {
+ final SparseArray<ProcessMemInfo> infoMap = new SparseArray<>(memInfos.size());
+ for (int i = 0, size = memInfos.size(); i < size; i++) {
+ ProcessMemInfo mi = memInfos.get(i);
+ infoMap.put(mi.pid, mi);
+ }
+ updateCpuStatsNow();
+ long[] memtrackTmp = new long[1];
+ long[] swaptrackTmp = new long[2];
+ // Get a list of Stats that have vsize > 0
+ final List<ProcessCpuTracker.Stats> stats = getCpuStats(st -> st.vsize > 0);
+ final int statsCount = stats.size();
+ for (int i = 0; i < statsCount; i++) {
+ ProcessCpuTracker.Stats st = stats.get(i);
+ long pss = Debug.getPss(st.pid, swaptrackTmp, memtrackTmp);
+ if (pss > 0) {
+ if (infoMap.indexOfKey(st.pid) < 0) {
+ ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid,
+ ProcessList.NATIVE_ADJ, -1, "native", null);
+ mi.pss = pss;
+ mi.swapPss = swaptrackTmp[1];
+ mi.memtrack = memtrackTmp[0];
+ memInfos.add(mi);
+ }
+ }
+ }
+
+ long totalPss = 0;
+ long totalSwapPss = 0;
+ long totalMemtrack = 0;
+ for (int i = 0, size = memInfos.size(); i < size; i++) {
+ ProcessMemInfo mi = memInfos.get(i);
+ if (mi.pss == 0) {
+ mi.pss = Debug.getPss(mi.pid, swaptrackTmp, memtrackTmp);
+ mi.swapPss = swaptrackTmp[1];
+ mi.memtrack = memtrackTmp[0];
+ }
+ totalPss += mi.pss;
+ totalSwapPss += mi.swapPss;
+ totalMemtrack += mi.memtrack;
+ }
+ Collections.sort(memInfos, new Comparator<ProcessMemInfo>() {
+ @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) {
+ if (lhs.oomAdj != rhs.oomAdj) {
+ return lhs.oomAdj < rhs.oomAdj ? -1 : 1;
+ }
+ if (lhs.pss != rhs.pss) {
+ return lhs.pss < rhs.pss ? 1 : -1;
+ }
+ return 0;
+ }
+ });
+
+ StringBuilder tag = new StringBuilder(128);
+ StringBuilder stack = new StringBuilder(128);
+ tag.append("Low on memory -- ");
+ appendMemBucket(tag, totalPss, "total", false);
+ appendMemBucket(stack, totalPss, "total", true);
+
+ StringBuilder fullNativeBuilder = new StringBuilder(1024);
+ StringBuilder shortNativeBuilder = new StringBuilder(1024);
+ StringBuilder fullJavaBuilder = new StringBuilder(1024);
+
+ boolean firstLine = true;
+ int lastOomAdj = Integer.MIN_VALUE;
+ long extraNativeRam = 0;
+ long extraNativeMemtrack = 0;
+ long cachedPss = 0;
+ for (int i = 0, size = memInfos.size(); i < size; i++) {
+ ProcessMemInfo mi = memInfos.get(i);
+
+ if (mi.oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+ cachedPss += mi.pss;
+ }
+
+ if (mi.oomAdj != ProcessList.NATIVE_ADJ
+ && (mi.oomAdj < ProcessList.SERVICE_ADJ
+ || mi.oomAdj == ProcessList.HOME_APP_ADJ
+ || mi.oomAdj == ProcessList.PREVIOUS_APP_ADJ)) {
+ if (lastOomAdj != mi.oomAdj) {
+ lastOomAdj = mi.oomAdj;
+ if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) {
+ tag.append(" / ");
+ }
+ if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ) {
+ if (firstLine) {
+ stack.append(":");
+ firstLine = false;
+ }
+ stack.append("\n\t at ");
+ } else {
+ stack.append("$");
+ }
+ } else {
+ tag.append(" ");
+ stack.append("$");
+ }
+ if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) {
+ appendMemBucket(tag, mi.pss, mi.name, false);
+ }
+ appendMemBucket(stack, mi.pss, mi.name, true);
+ if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ
+ && ((i + 1) >= size || memInfos.get(i + 1).oomAdj != lastOomAdj)) {
+ stack.append("(");
+ for (int k = 0; k < DUMP_MEM_OOM_ADJ.length; k++) {
+ if (DUMP_MEM_OOM_ADJ[k] == mi.oomAdj) {
+ stack.append(DUMP_MEM_OOM_LABEL[k]);
+ stack.append(":");
+ stack.append(DUMP_MEM_OOM_ADJ[k]);
+ }
+ }
+ stack.append(")");
+ }
+ }
+
+ appendMemInfo(fullNativeBuilder, mi);
+ if (mi.oomAdj == ProcessList.NATIVE_ADJ) {
+ // The short form only has native processes that are >= 512K.
+ if (mi.pss >= 512) {
+ appendMemInfo(shortNativeBuilder, mi);
+ } else {
+ extraNativeRam += mi.pss;
+ extraNativeMemtrack += mi.memtrack;
+ }
+ } else {
+ // Short form has all other details, but if we have collected RAM
+ // from smaller native processes let's dump a summary of that.
+ if (extraNativeRam > 0) {
+ appendBasicMemEntry(shortNativeBuilder, ProcessList.NATIVE_ADJ,
+ -1, extraNativeRam, extraNativeMemtrack, "(Other native)");
+ shortNativeBuilder.append('\n');
+ extraNativeRam = 0;
+ }
+ appendMemInfo(fullJavaBuilder, mi);
+ }
+ }
+
+ fullJavaBuilder.append(" ");
+ ProcessList.appendRamKb(fullJavaBuilder, totalPss);
+ fullJavaBuilder.append(": TOTAL");
+ if (totalMemtrack > 0) {
+ fullJavaBuilder.append(" (");
+ fullJavaBuilder.append(stringifyKBSize(totalMemtrack));
+ fullJavaBuilder.append(" memtrack)");
+ }
+ fullJavaBuilder.append("\n");
+
+ MemInfoReader memInfo = new MemInfoReader();
+ memInfo.readMemInfo();
+ final long[] infos = memInfo.getRawInfo();
+
+ StringBuilder memInfoBuilder = new StringBuilder(1024);
+ Debug.getMemInfo(infos);
+ memInfoBuilder.append(" MemInfo: ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SLAB])).append(" slab, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SHMEM])).append(" shmem, ");
+ memInfoBuilder.append(stringifyKBSize(
+ infos[Debug.MEMINFO_VM_ALLOC_USED])).append(" vm alloc, ");
+ memInfoBuilder.append(stringifyKBSize(
+ infos[Debug.MEMINFO_PAGE_TABLES])).append(" page tables ");
+ memInfoBuilder.append(stringifyKBSize(
+ infos[Debug.MEMINFO_KERNEL_STACK])).append(" kernel stack\n");
+ memInfoBuilder.append(" ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_BUFFERS])).append(" buffers, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_CACHED])).append(" cached, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_MAPPED])).append(" mapped, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_FREE])).append(" free\n");
+ if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) {
+ memInfoBuilder.append(" ZRAM: ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_ZRAM_TOTAL]));
+ memInfoBuilder.append(" RAM, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_TOTAL]));
+ memInfoBuilder.append(" swap total, ");
+ memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_FREE]));
+ memInfoBuilder.append(" swap free\n");
+ }
+ final long[] ksm = getKsmInfo();
+ if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0
+ || ksm[KSM_VOLATILE] != 0) {
+ memInfoBuilder.append(" KSM: ");
+ memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARING]));
+ memInfoBuilder.append(" saved from shared ");
+ memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARED]));
+ memInfoBuilder.append("\n ");
+ memInfoBuilder.append(stringifyKBSize(ksm[KSM_UNSHARED]));
+ memInfoBuilder.append(" unshared; ");
+ memInfoBuilder.append(stringifyKBSize(ksm[KSM_VOLATILE]));
+ memInfoBuilder.append(" volatile\n");
+ }
+ memInfoBuilder.append(" Free RAM: ");
+ memInfoBuilder.append(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
+ + memInfo.getFreeSizeKb()));
+ memInfoBuilder.append("\n");
+ long kernelUsed = memInfo.getKernelUsedSizeKb();
+ final long ionHeap = Debug.getIonHeapsSizeKb();
+ final long ionPool = Debug.getIonPoolsSizeKb();
+ if (ionHeap >= 0 && ionPool >= 0) {
+ final long ionMapped = Debug.getIonMappedSizeKb();
+ final long ionUnmapped = ionHeap - ionMapped;
+ memInfoBuilder.append(" ION: ");
+ memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
+ memInfoBuilder.append("\n");
+ // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
+ // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
+ kernelUsed += ionHeap;
+ }
+ final long gpuUsage = Debug.getGpuTotalUsageKb();
+ if (gpuUsage >= 0) {
+ memInfoBuilder.append(" GPU: ");
+ memInfoBuilder.append(stringifyKBSize(gpuUsage));
+ memInfoBuilder.append("\n");
+ }
+ memInfoBuilder.append(" Used RAM: ");
+ memInfoBuilder.append(stringifyKBSize(
+ totalPss - cachedPss + kernelUsed));
+ memInfoBuilder.append("\n");
+ memInfoBuilder.append(" Lost RAM: ");
+ memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
+ - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+ - kernelUsed - memInfo.getZramTotalSizeKb()));
+ memInfoBuilder.append("\n");
+ Slog.i(TAG, "Low on memory:");
+ Slog.i(TAG, shortNativeBuilder.toString());
+ Slog.i(TAG, fullJavaBuilder.toString());
+ Slog.i(TAG, memInfoBuilder.toString());
+
+ StringBuilder dropBuilder = new StringBuilder(1024);
+ dropBuilder.append("Low on memory:");
+ dropBuilder.append(stack);
+ dropBuilder.append('\n');
+ dropBuilder.append(fullNativeBuilder);
+ dropBuilder.append(fullJavaBuilder);
+ dropBuilder.append('\n');
+ dropBuilder.append(memInfoBuilder);
+ dropBuilder.append('\n');
+ StringWriter catSw = new StringWriter();
+ synchronized (mService) {
+ PrintWriter catPw = new FastPrintWriter(catSw, false, 256);
+ String[] emptyArgs = new String[] { };
+ catPw.println();
+ mService.mProcessList.dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null, -1);
+ catPw.println();
+ mService.mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0,
+ false, null).dumpLocked();
+ catPw.println();
+ mService.mAtmInternal.dump(
+ DUMP_ACTIVITIES_CMD, null, catPw, emptyArgs, 0, false, false, null);
+ catPw.flush();
+ }
+ dropBuilder.append(catSw.toString());
+ FrameworkStatsLog.write(FrameworkStatsLog.LOW_MEM_REPORTED);
+ mService.addErrorToDropBox("lowmem", null, "system_server", null,
+ null, null, tag.toString(), dropBuilder.toString(), null, null);
+ synchronized (mService) {
+ long now = SystemClock.uptimeMillis();
+ if (mLastMemUsageReportTime < now) {
+ mLastMemUsageReportTime = now;
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ private void stopProfilerLPf(ProcessRecord proc, int profileType) {
if (proc == null || proc == mProfileData.getProfileProc()) {
proc = mProfileData.getProfileProc();
profileType = mProfileType;
- clearProfilerLocked();
+ clearProfilerLPf();
}
if (proc == null) {
return;
}
+ final IApplicationThread thread = proc.mProfile.getThread();
+ if (thread == null) {
+ return;
+ }
try {
- proc.thread.profilerControl(false, null, profileType);
+ thread.profilerControl(false, null, profileType);
} catch (RemoteException e) {
throw new IllegalStateException("Process disappeared");
}
}
- @GuardedBy("mService")
- void clearProfilerLocked() {
+ @GuardedBy("mProfilerLock")
+ void clearProfilerLPf() {
if (mProfileData.getProfilerInfo() != null
&& mProfileData.getProfilerInfo().profileFd != null) {
try {
@@ -1118,22 +1610,22 @@
mProfileData.setProfilerInfo(null);
}
- @GuardedBy("mService")
- void clearProfilerLocked(ProcessRecord app) {
+ @GuardedBy("mProfilerLock")
+ void clearProfilerLPf(ProcessRecord app) {
if (mProfileData.getProfileProc() == null
|| mProfileData.getProfilerInfo() == null
|| mProfileData.getProfileProc() != app) {
return;
}
- clearProfilerLocked();
+ clearProfilerLPf();
}
- @GuardedBy("mService")
- boolean profileControlLocked(ProcessRecord proc, boolean start,
+ @GuardedBy("mProfilerLock")
+ boolean profileControlLPf(ProcessRecord proc, boolean start,
ProfilerInfo profilerInfo, int profileType) {
try {
if (start) {
- stopProfilerLocked(null, 0);
+ stopProfilerLPf(null, 0);
mService.setProfileApp(proc.info, proc.processName, profilerInfo);
mProfileData.setProfileProc(proc);
mProfileType = profileType;
@@ -1144,7 +1636,7 @@
fd = null;
}
profilerInfo.profileFd = fd;
- proc.thread.profilerControl(start, profilerInfo, profileType);
+ proc.mProfile.getThread().profilerControl(start, profilerInfo, profileType);
fd = null;
try {
mProfileData.getProfilerInfo().profileFd.close();
@@ -1160,7 +1652,7 @@
profilerInfo = null;
}
} else {
- stopProfilerLocked(proc, profileType);
+ stopProfilerLPf(proc, profileType);
if (profilerInfo != null && profilerInfo.profileFd != null) {
try {
profilerInfo.profileFd.close();
@@ -1182,8 +1674,8 @@
}
}
- @GuardedBy("mService")
- void setProfileAppLocked(String processName, ProfilerInfo profilerInfo) {
+ @GuardedBy("mProfilerLock")
+ void setProfileAppLPf(String processName, ProfilerInfo profilerInfo) {
mProfileData.setProfileApp(processName);
if (mProfileData.getProfilerInfo() != null) {
@@ -1198,13 +1690,13 @@
mProfileType = 0;
}
- @GuardedBy("mService")
- void setProfileProcLocked(ProcessRecord proc) {
+ @GuardedBy("mProfilerLock")
+ void setProfileProcLPf(ProcessRecord proc) {
mProfileData.setProfileProc(proc);
}
- @GuardedBy("mService")
- void setAgentAppLocked(@NonNull String packageName, @Nullable String agent) {
+ @GuardedBy("mProfilerLock")
+ void setAgentAppLPf(@NonNull String packageName, @Nullable String agent) {
if (agent == null) {
if (mAppAgentMap != null) {
mAppAgentMap.remove(packageName);
@@ -1226,8 +1718,7 @@
}
}
- @GuardedBy("mService")
- void updateCpuStatsLocked() {
+ void updateCpuStats() {
final long now = SystemClock.uptimeMillis();
if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) {
return;
@@ -1301,17 +1792,18 @@
totalUTime += st.rel_utime;
totalSTime += st.rel_stime;
if (pr != null) {
- BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats;
+ final ProcessProfileRecord profile = pr.mProfile;
+ BatteryStatsImpl.Uid.Proc ps = profile.getCurProcBatteryStats();
if (ps == null || !ps.isActive()) {
- pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked(
+ profile.setCurProcBatteryStats(
+ ps = bstats.getProcessStatsLocked(
pr.info.uid, pr.processName,
- elapsedRealtime, uptime);
+ elapsedRealtime, uptime));
}
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
- pr.curCpuTime += st.rel_utime + st.rel_stime;
- if (pr.lastCpuTime == 0) {
- pr.lastCpuTime = pr.curCpuTime;
- }
+ final long curCpuTime = profile.mCurCpuTime.addAndGet(
+ st.rel_utime + st.rel_stime);
+ profile.mLastCpuTime.compareAndSet(0, curCpuTime);
} else {
BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
if (ps == null || !ps.isActive()) {
@@ -1483,44 +1975,46 @@
ProfilerInfo profilerInfo = null;
String preBindAgent = null;
final String processName = app.processName;
- if (mProfileData.getProfileApp() != null
- && mProfileData.getProfileApp().equals(processName)) {
- mProfileData.setProfileProc(app);
- if (mProfileData.getProfilerInfo() != null) {
- // Send a profiler info object to the app if either a file is given, or
- // an agent should be loaded at bind-time.
- boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null
- || mProfileData.getProfilerInfo().attachAgentDuringBind;
- profilerInfo = needsInfo
- ? new ProfilerInfo(mProfileData.getProfilerInfo()) : null;
- if (mProfileData.getProfilerInfo().agent != null) {
- preBindAgent = mProfileData.getProfilerInfo().agent;
+ synchronized (mProfilerLock) {
+ if (mProfileData.getProfileApp() != null
+ && mProfileData.getProfileApp().equals(processName)) {
+ mProfileData.setProfileProc(app);
+ if (mProfileData.getProfilerInfo() != null) {
+ // Send a profiler info object to the app if either a file is given, or
+ // an agent should be loaded at bind-time.
+ boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null
+ || mProfileData.getProfilerInfo().attachAgentDuringBind;
+ profilerInfo = needsInfo
+ ? new ProfilerInfo(mProfileData.getProfilerInfo()) : null;
+ if (mProfileData.getProfilerInfo().agent != null) {
+ preBindAgent = mProfileData.getProfilerInfo().agent;
+ }
+ }
+ } else if (instr != null && instr.mProfileFile != null) {
+ profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
+ null, false);
+ }
+ if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
+ // We need to do a debuggable check here. See setAgentApp for why the check is
+ // postponed to here.
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ String agent = mAppAgentMap.get(processName);
+ // Do not overwrite already requested agent.
+ if (profilerInfo == null) {
+ profilerInfo = new ProfilerInfo(null, null, 0, false, false,
+ mAppAgentMap.get(processName), true);
+ } else if (profilerInfo.agent == null) {
+ profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
+ }
}
}
- } else if (instr != null && instr.mProfileFile != null) {
- profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
- null, false);
- }
- if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
- // We need to do a debuggable check here. See setAgentApp for why the check is
- // postponed to here.
- if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- String agent = mAppAgentMap.get(processName);
- // Do not overwrite already requested agent.
- if (profilerInfo == null) {
- profilerInfo = new ProfilerInfo(null, null, 0, false, false,
- mAppAgentMap.get(processName), true);
- } else if (profilerInfo.agent == null) {
- profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
- }
- }
- }
- if (profilerInfo != null && profilerInfo.profileFd != null) {
- profilerInfo.profileFd = profilerInfo.profileFd.dup();
- if (TextUtils.equals(mProfileData.getProfileApp(), processName)
- && mProfileData.getProfilerInfo() != null) {
- clearProfilerLocked();
+ if (profilerInfo != null && profilerInfo.profileFd != null) {
+ profilerInfo.profileFd = profilerInfo.profileFd.dup();
+ if (TextUtils.equals(mProfileData.getProfileApp(), processName)
+ && mProfileData.getProfilerInfo() != null) {
+ clearProfilerLPf();
+ }
}
}
@@ -1566,19 +2060,25 @@
@GuardedBy("mService")
void onCleanupApplicationRecordLocked(ProcessRecord app) {
- mPendingPssProcesses.remove(app);
- abortNextPssTime(app.procStateMemTracker);
- }
-
- @GuardedBy("mService")
- void onAppDiedLocked(ProcessRecord app) {
- if (mProfileData.getProfileProc() == app) {
- clearProfilerLocked();
+ synchronized (mProfilerLock) {
+ final ProcessProfileRecord profile = app.mProfile;
+ mProcessesToGc.remove(app);
+ mPendingPssProfiles.remove(profile);
+ profile.abortNextPssTime();
}
}
@GuardedBy("mService")
- boolean dumpMemWatchProcessesLocked(PrintWriter pw, boolean needSep) {
+ void onAppDiedLocked(ProcessRecord app) {
+ synchronized (mProfilerLock) {
+ if (mProfileData.getProfileProc() == app) {
+ clearProfilerLPf();
+ }
+ }
+ }
+
+ @GuardedBy("mProfilerLock")
+ boolean dumpMemWatchProcessesLPf(PrintWriter pw, boolean needSep) {
if (mMemWatchProcesses.getMap().size() > 0) {
pw.println(" Mem watch processes:");
final ArrayMap<String, SparseArray<Pair<Long, String>>> procs =
@@ -1669,8 +2169,8 @@
+ " mLastNumProcesses=" + mLastNumProcesses);
}
- @GuardedBy("mService")
- void writeMemWatchProcessToProtoLocked(ProtoOutputStream proto) {
+ @GuardedBy("mProfilerLock")
+ void writeMemWatchProcessToProtoLPf(ProtoOutputStream proto) {
if (mMemWatchProcesses.getMap().size() > 0) {
final long token = proto.start(
ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
@@ -1753,4 +2253,55 @@
report.append(mProcessCpuTracker.printCurrentState(time));
}
}
+
+ @GuardedBy("mProfilerLock")
+ void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
+ if (mProcessesToGc.size() > 0) {
+ long now = SystemClock.uptimeMillis();
+ for (int i = 0, size = mProcessesToGc.size(); i < size; i++) {
+ ProcessRecord r = mProcessesToGc.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+ continue;
+ }
+ final long token = proto.start(fieldId);
+ final ProcessProfileRecord profile = r.mProfile;
+ r.dumpDebug(proto, ProcessToGcProto.PROC);
+ proto.write(ProcessToGcProto.REPORT_LOW_MEMORY, profile.getReportLowMemory());
+ proto.write(ProcessToGcProto.NOW_UPTIME_MS, now);
+ proto.write(ProcessToGcProto.LAST_GCED_MS, profile.getLastRequestedGc());
+ proto.write(ProcessToGcProto.LAST_LOW_MEMORY_MS, profile.getLastLowMemory());
+ proto.end(token);
+ }
+ }
+ }
+
+ @GuardedBy("mProfilerLock")
+ boolean dumpProcessesToGc(PrintWriter pw, boolean needSep, String dumpPackage) {
+ if (mProcessesToGc.size() > 0) {
+ boolean printed = false;
+ long now = SystemClock.uptimeMillis();
+ for (int i = 0, size = mProcessesToGc.size(); i < size; i++) {
+ ProcessRecord proc = mProcessesToGc.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(proc.info.packageName)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println();
+ needSep = true;
+ pw.println(" Processes that are waiting to GC:");
+ printed = true;
+ }
+ pw.print(" Process "); pw.println(proc);
+ final ProcessProfileRecord profile = proc.mProfile;
+ pw.print(" lowMem="); pw.print(profile.getReportLowMemory());
+ pw.print(", last gced=");
+ pw.print(now - profile.getLastRequestedGc());
+ pw.print(" ms ago, last lowMem=");
+ pw.print(now - profile.getLastLowMemory());
+ pw.println(" ms ago");
+
+ }
+ }
+ return needSep;
+ }
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index b153cfd..c6947c2d 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -43,6 +43,7 @@
import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.power.MeasuredEnergyArray;
import com.android.internal.power.MeasuredEnergyStats;
@@ -96,8 +97,6 @@
return t;
});
- private final Context mContext;
-
@GuardedBy("mStats")
private final BatteryStatsImpl mStats;
@@ -168,15 +167,39 @@
@GuardedBy("this")
private long mLastCollectionTimeStamp;
+ final Injector mInjector;
+
+ @VisibleForTesting
+ public static class Injector {
+ private final Context mContext;
+
+ Injector(Context context) {
+ mContext = context;
+ }
+
+ public <T> T getSystemService(Class<T> serviceClass) {
+ return mContext.getSystemService(serviceClass);
+ }
+
+ public <T> T getLocalService(Class<T> serviceClass) {
+ return LocalServices.getService(serviceClass);
+ }
+ }
+
BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) {
- mContext = context;
+ this(new Injector(context), stats);
+ }
+
+ @VisibleForTesting
+ BatteryExternalStatsWorker(Injector injector, BatteryStatsImpl stats) {
+ mInjector = injector;
mStats = stats;
}
public void systemServicesReady() {
- final WifiManager wm = mContext.getSystemService(WifiManager.class);
- final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
- final PowerStatsInternal psi = LocalServices.getService(PowerStatsInternal.class);
+ final WifiManager wm = mInjector.getSystemService(WifiManager.class);
+ final TelephonyManager tm = mInjector.getSystemService(TelephonyManager.class);
+ final PowerStatsInternal psi = mInjector.getLocalService(PowerStatsInternal.class);
synchronized (mWorkerLock) {
mWifiManager = wm;
mTelephony = tm;
@@ -184,12 +207,13 @@
if (mPowerStatsInternal != null) {
populateEnergyConsumerSubsystemMapsLocked();
final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData();
- final boolean[] supportedBuckets = getSupportedEnergyBuckets(
- initialMeasuredEnergies);
mMeasuredEnergySnapshot = initialMeasuredEnergies == null
? null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
+ final boolean[] supportedStdBuckets
+ = getSupportedEnergyBuckets(initialMeasuredEnergies);
+ final int numCustomBuckets = 0; // TODO: Get this from initialMeasuredEnergies
synchronized (mStats) {
- mStats.initMeasuredEnergyStatsLocked(supportedBuckets);
+ mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, numCustomBuckets);
}
}
}
@@ -717,15 +741,16 @@
/**
* Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to
- * their corresponding {@link MeasuredEnergyStats.EnergyBucket}s.
+ * their corresponding {@link MeasuredEnergyStats.StandardEnergyBucket}s.
+ * Does not include custom energy buckets (which are always, by definition, supported).
*
- * @return array with true for index i if energy bucket i is supported.
+ * @return array with true for index i if standard energy bucket i is supported.
*/
private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
if (energyArray == null) {
return null;
}
- final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
+ final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS];
final int size = energyArray.size();
for (int energyIdx = 0; energyIdx < size; energyIdx++) {
switch (energyArray.getSubsystem(energyIdx)) {
@@ -747,7 +772,8 @@
* EnergyConsumerResult}[]
*/
@GuardedBy("mWorkerLock")
- private @Nullable MeasuredEnergyArray getEnergyConsumptionData() {
+ @VisibleForTesting
+ public @Nullable MeasuredEnergyArray getEnergyConsumptionData() {
final EnergyConsumerResult[] results;
try {
results = mPowerStatsInternal.getEnergyConsumedAsync(new int[0])
@@ -761,28 +787,41 @@
final int[] subsystems = new int[size];
final long[] energyUJ = new long[size];
+ int count = 0;
for (int i = 0; i < size; i++) {
final EnergyConsumerResult consumer = results[i];
final int subsystem = mEnergyConsumerToSubsystemMap.get(consumer.id,
MeasuredEnergyArray.SUBSYSTEM_UNKNOWN);
if (subsystem == MeasuredEnergyArray.SUBSYSTEM_UNKNOWN) continue;
- subsystems[i] = subsystem;
- energyUJ[i] = consumer.energyUWs;
+ subsystems[count] = subsystem;
+ energyUJ[count] = consumer.energyUWs;
+ count++;
}
+ final int arraySize = count;
return new MeasuredEnergyArray() {
@Override
public int getSubsystem(int index) {
+ if (index >= size()) {
+ throw new IllegalArgumentException(
+ "Out of bounds subsystem index! index : " + index + ", size : "
+ + size());
+ }
return subsystems[index];
}
@Override
public long getEnergy(int index) {
+ if (index >= size()) {
+ throw new IllegalArgumentException(
+ "Out of bounds subsystem index! index : " + index + ", size : "
+ + size());
+ }
return energyUJ[index];
}
@Override
public int size() {
- return size;
+ return arraySize;
}
};
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c287240..382381d 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -21,6 +21,10 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.State;
+import android.hardware.power.stats.StateResidency;
+import android.hardware.power.stats.StateResidencyResult;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkCapabilities;
import android.os.BatteryStats;
@@ -51,6 +55,7 @@
import android.os.health.HealthStatsParceler;
import android.os.health.HealthStatsWriter;
import android.os.health.UidHealthStats;
+import android.power.PowerStatsInternal;
import android.provider.Settings;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
@@ -87,10 +92,13 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
/**
* All information we are collecting about things that can happen that impact
@@ -112,23 +120,23 @@
private final BatteryExternalStatsWorker mWorker;
private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
- private native void getLowPowerStats(RpmStats rpmStats);
- private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
- private native int getSubsystemLowPowerStats(ByteBuffer outBuffer);
private native void getRailEnergyPowerStats(RailStats railStats);
private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8
.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith("?");
- private ByteBuffer mUtf8BufferStat = ByteBuffer.allocateDirect(MAX_LOW_POWER_STATS_SIZE);
- private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
+ private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Object mLock = new Object();
+ private PowerStatsInternal mPowerStatsInternal = null;
+ private Map<Integer, String> mEntityNames = new HashMap();
+ private Map<Integer, Map<Integer, String>> mStateNames = new HashMap();
+
@GuardedBy("mStats")
private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
@GuardedBy("mStats")
@@ -163,16 +171,57 @@
}
};
+ private void populatePowerEntityMaps() {
+ if (mPowerStatsInternal == null) {
+ // PowerStatsInternal unavailable, don't bother populating maps.
+ mEntityNames = null;
+ mStateNames = null;
+ return;
+ }
+
+ PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
+ if (entities == null) {
+ return;
+ }
+
+ for (int i = 0; i < entities.length; i++) {
+ final PowerEntity entity = entities[i];
+ Map<Integer, String> states = new HashMap();
+ for (int j = 0; j < entity.states.length; j++) {
+ final State state = entity.states[j];
+ states.put(state.id, state.name);
+ }
+
+ mEntityNames.put(entity.id, entity.name);
+ mStateNames.put(entity.id, states);
+ }
+ }
+
/**
* Replaces the information in the given rpmStats with up-to-date information.
*/
@Override
public void fillLowPowerStats(RpmStats rpmStats) {
- if (DBG) Slog.d(TAG, "begin getLowPowerStats");
+ final StateResidencyResult[] results;
try {
- getLowPowerStats(rpmStats);
- } finally {
- if (DBG) Slog.d(TAG, "end getLowPowerStats");
+ results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
+ .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to getStateResidencyAsync", e);
+ return;
+ }
+
+ for (int i = 0; i < results.length; i++) {
+ final StateResidencyResult result = results[i];
+ RpmStats.PowerStateSubsystem subsystem =
+ rpmStats.getSubsystem(mEntityNames.get(result.id));
+
+ for (int j = 0; j < result.stateResidencyData.length; j++) {
+ final StateResidency stateResidency = result.stateResidencyData[j];
+ subsystem.putState(mStateNames.get(result.id).get(stateResidency.id),
+ stateResidency.totalTimeInStateMs,
+ (int) stateResidency.totalStateEntryCount);
+ }
}
}
@@ -187,47 +236,46 @@
}
@Override
- public String getPlatformLowPowerStats() {
- if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats");
- try {
- mUtf8BufferStat.clear();
- mUtf16BufferStat.clear();
- mDecoderStat.reset();
- int bytesWritten = getPlatformLowPowerStats(mUtf8BufferStat);
- if (bytesWritten < 0) {
- return null;
- } else if (bytesWritten == 0) {
- return "Empty";
- }
- mUtf8BufferStat.limit(bytesWritten);
- mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true);
- mUtf16BufferStat.flip();
- return mUtf16BufferStat.toString();
- } finally {
- if (DBG) Slog.d(TAG, "end getPlatformLowPowerStats");
- }
- }
-
- @Override
public String getSubsystemLowPowerStats() {
- if (DBG) Slog.d(TAG, "begin getSubsystemLowPowerStats");
+ final StateResidencyResult[] results;
try {
- mUtf8BufferStat.clear();
- mUtf16BufferStat.clear();
- mDecoderStat.reset();
- int bytesWritten = getSubsystemLowPowerStats(mUtf8BufferStat);
- if (bytesWritten < 0) {
- return null;
- } else if (bytesWritten == 0) {
- return "Empty";
- }
- mUtf8BufferStat.limit(bytesWritten);
- mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true);
- mUtf16BufferStat.flip();
- return mUtf16BufferStat.toString();
- } finally {
- if (DBG) Slog.d(TAG, "end getSubsystemLowPowerStats");
+ results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
+ .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to getStateResidencyAsync", e);
+ return "Empty";
}
+
+ if (results.length == 0) return "Empty";
+
+ int charsLeft = MAX_LOW_POWER_STATS_SIZE;
+ StringBuilder builder = new StringBuilder("SubsystemPowerState");
+ for (int i = 0; i < results.length; i++) {
+ final StateResidencyResult result = results[i];
+ StringBuilder subsystemBuilder = new StringBuilder();
+ subsystemBuilder.append(" subsystem_" + i);
+ subsystemBuilder.append(" name=" + mEntityNames.get(result.id));
+
+ for (int j = 0; j < result.stateResidencyData.length; j++) {
+ final StateResidency stateResidency = result.stateResidencyData[j];
+ subsystemBuilder.append(" state_" + j);
+ subsystemBuilder.append(" name=" + mStateNames.get(result.id).get(
+ stateResidency.id));
+ subsystemBuilder.append(" time=" + stateResidency.totalTimeInStateMs);
+ subsystemBuilder.append(" count=" + stateResidency.totalStateEntryCount);
+ subsystemBuilder.append(" last entry=" + stateResidency.lastEntryTimestampMs);
+ }
+
+ if (subsystemBuilder.length() <= charsLeft) {
+ charsLeft -= subsystemBuilder.length();
+ builder.append(subsystemBuilder);
+ } else {
+ Slog.e(TAG, "getSubsystemLowPowerStats: buffer not enough");
+ break;
+ }
+ }
+
+ return builder.toString();
}
BatteryStatsService(Context context, File systemDir, Handler handler) {
@@ -274,6 +322,11 @@
} catch (RemoteException e) {
Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
}
+ mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
+ if (mPowerStatsInternal != null) {
+ populatePowerEntityMaps();
+ }
+
Watchdog.getInstance().addMonitor(this);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index d2ee69e..fca3a52 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -42,6 +42,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -849,7 +850,7 @@
private boolean requestStartTargetPermissionsReviewIfNeededLocked(
BroadcastRecord receiverRecord, String receivingPackageName,
final int receivingUserId) {
- if (!mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+ if (!mService.getPackageManagerInternal().isPermissionsReviewRequired(
receivingPackageName, receivingUserId)) {
return true;
}
@@ -897,7 +898,7 @@
}
final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r,
- @BroadcastOptions.TempAllowListType int type) {
+ @TempAllowListType int type) {
if (duration > Integer.MAX_VALUE) {
duration = Integer.MAX_VALUE;
}
@@ -922,7 +923,7 @@
Slog.v(TAG, "Broadcast temp whitelist uid=" + uid + " duration=" + duration
+ " type=" + type + " : " + b.toString());
}
- mService.tempWhitelistUidLocked(uid, duration, b.toString(), type);
+ mService.tempAllowlistUidLocked(uid, duration, b.toString(), type);
}
/**
@@ -969,7 +970,7 @@
+ mParallelBroadcasts.size() + " parallel broadcasts; "
+ mDispatcher.describeStateLocked());
- mService.updateCpuStatsLocked();
+ mService.updateCpuStats();
if (fromMsg) {
mBroadcastsScheduled = false;
@@ -1050,7 +1051,9 @@
if (r == null) {
// No more broadcasts are deliverable right now, so all done!
mDispatcher.scheduleDeferralCheckLocked(false);
- mService.scheduleAppGcsLocked();
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ mService.mAppProfiler.scheduleAppGcsLPf();
+ }
if (looped && !skipOomAdj) {
// If we had finished the last ordered broadcast, then
// make sure all processes have correct oom and sched
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index 26cfd62..45ce4c5 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -60,6 +60,9 @@
private final Object mPhenotypeFlagLock = new Object();
+ private final ActivityManagerService mService;
+ private final Object mProfilerLock;
+
@GuardedBy("mPhenotypeFlagLock")
private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
// Weight to apply to the LRU ordering.
@@ -101,6 +104,11 @@
}
};
+ CacheOomRanker(final ActivityManagerService service) {
+ mService = service;
+ mProfilerLock = service.mAppProfiler.mProfilerLock;
+ }
+
/** Load settings from device config and register a listener for changes. */
public void init(Executor executor) {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -223,7 +231,9 @@
addToScore(scoredProcessRecords, lruWeight);
}
if (rssWeight > 0.0f) {
- Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
+ }
addToScore(scoredProcessRecords, rssWeight);
}
if (usesWeight > 0.0f) {
@@ -297,7 +307,7 @@
@Override
public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
// High RSS first to match least recently used.
- return Long.compare(o2.proc.mLastRss, o1.proc.mLastRss);
+ return Long.compare(o2.proc.mProfile.getLastRss(), o1.proc.mProfile.getLastRss());
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 27a238d..c558b3d 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -741,10 +741,12 @@
// This will ensure app will be out of the freezer for at least FREEZE_TIMEOUT_MS
void unfreezeTemporarily(ProcessRecord app) {
- synchronized (mAm) {
- if (app.frozen) {
- unfreezeAppLocked(app);
- freezeAppAsync(app);
+ if (mUseFreezer) {
+ synchronized (mAm) {
+ if (app.frozen) {
+ unfreezeAppLocked(app);
+ freezeAppAsync(app);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 6d9d3fb..e6cd509 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -128,7 +128,7 @@
&& association == null && binding.service.app != null
&& (binding.service.appInfo.uid != clientUid
|| !binding.service.processName.equals(clientProcessName))) {
- ProcessStats.ProcessStateHolder holder = binding.service.app.pkgList.get(
+ ProcessStats.ProcessStateHolder holder = binding.service.app.getPkgList().get(
binding.service.instanceName.getPackageName());
if (holder == null) {
Slog.wtf(TAG_AM, "No package in referenced service "
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index be49ce4..efee432 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+
import android.os.Binder;
import android.os.SystemClock;
import android.util.Slog;
@@ -25,9 +28,6 @@
import com.android.internal.app.procstats.AssociationState;
import com.android.internal.app.procstats.ProcessStats;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-
/**
* Represents a link between a content provider and client.
*/
@@ -71,7 +71,7 @@
&& association == null && provider.proc != null
&& (provider.appInfo.uid != client.uid
|| !provider.info.processName.equals(client.processName))) {
- ProcessStats.ProcessStateHolder holder = provider.proc.pkgList.get(
+ ProcessStats.ProcessStateHolder holder = provider.proc.getPkgList().get(
provider.name.getPackageName());
if (holder == null) {
Slog.wtf(TAG_AM, "No package in referenced provider "
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 89ed423..478c512 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -1214,9 +1214,8 @@
final ProcessRecord app = apps.valueAt(iApp);
if (app.userId != userId || app.thread == null || app.unlocked) continue;
- for (int iPkg = 0, numPkgs = app.pkgList.size(); iPkg < numPkgs; iPkg++) {
+ app.getPkgList().forEachPackage(pkgName -> {
try {
- final String pkgName = app.pkgList.keyAt(iPkg);
final PackageInfo pkgInfo = AppGlobals.getPackageManager()
.getPackageInfo(pkgName, matchFlags, userId);
if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
@@ -1243,7 +1242,7 @@
}
} catch (RemoteException ignored) {
}
- }
+ });
}
}
}
@@ -1430,13 +1429,14 @@
return mService.validateAssociationAllowedLocked(cpi.packageName,
cpi.applicationInfo.uid, null, callingUid) ? null : "<null>";
}
- for (int i = callingApp.pkgList.size() - 1; i >= 0; i--) {
- if (!mService.validateAssociationAllowedLocked(callingApp.pkgList.keyAt(i),
+ final String r = callingApp.getPkgList().forEachPackage(pkgName -> {
+ if (!mService.validateAssociationAllowedLocked(pkgName,
callingApp.uid, cpi.packageName, cpi.applicationInfo.uid)) {
return cpi.packageName;
}
- }
- return null;
+ return null;
+ });
+ return r;
}
ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId, int pmFlags) {
@@ -1531,7 +1531,7 @@
private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi,
ProcessRecord r, final int userId, Context context) {
- if (!mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+ if (!mService.getPackageManagerInternal().isPermissionsReviewRequired(
cpi.packageName, userId)) {
return true;
}
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index fb8b5d4..59a1939 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -343,7 +343,7 @@
&& mAssociation == null && provider.proc != null
&& (provider.appInfo.uid != mOwningUid
|| !provider.info.processName.equals(mOwningProcessName))) {
- ProcessStats.ProcessStateHolder holder = provider.proc.pkgList.get(
+ ProcessStats.ProcessStateHolder holder = provider.proc.getPkgList().get(
provider.name.getPackageName());
if (holder == null) {
Slog.wtf(TAG_AM, "No package in referenced provider "
diff --git a/services/core/java/com/android/server/am/FgsStartTempAllowList.java b/services/core/java/com/android/server/am/FgsStartTempAllowList.java
index 3aca4cf..4d8749c 100644
--- a/services/core/java/com/android/server/am/FgsStartTempAllowList.java
+++ b/services/core/java/com/android/server/am/FgsStartTempAllowList.java
@@ -70,4 +70,8 @@
return true;
}
}
+
+ void remove(int uid) {
+ mTempAllowListFgs.delete(uid);
+ }
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index de79315..c075336 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -51,6 +51,7 @@
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
@@ -65,10 +66,10 @@
import static com.android.server.am.ActivityManagerService.TAG_BACKUP;
import static com.android.server.am.ActivityManagerService.TAG_LRU;
import static com.android.server.am.ActivityManagerService.TAG_OOM_ADJ;
-import static com.android.server.am.ActivityManagerService.TAG_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerService.TOP_APP_PRIORITY_BOOST;
import static com.android.server.am.AppProfiler.TAG_PSS;
+import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import android.app.ActivityManager;
@@ -131,7 +132,7 @@
static final String OOM_ADJ_REASON_GET_PROVIDER = OOM_ADJ_REASON_METHOD + "_getProvider";
static final String OOM_ADJ_REASON_REMOVE_PROVIDER = OOM_ADJ_REASON_METHOD + "_removeProvider";
static final String OOM_ADJ_REASON_UI_VISIBILITY = OOM_ADJ_REASON_METHOD + "_uiVisibility";
- static final String OOM_ADJ_REASON_WHITELIST = OOM_ADJ_REASON_METHOD + "_whitelistChange";
+ static final String OOM_ADJ_REASON_ALLOWLIST = OOM_ADJ_REASON_METHOD + "_allowlistChange";
static final String OOM_ADJ_REASON_PROCESS_BEGIN = OOM_ADJ_REASON_METHOD + "_processBegin";
static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd";
@@ -337,7 +338,7 @@
mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
mConstants = mService.mConstants;
mCachedAppOptimizer = new CachedAppOptimizer(mService);
- mCacheOomRanker = new CacheOomRanker();
+ mCacheOomRanker = new CacheOomRanker(service);
mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
final int pid = msg.arg1;
@@ -394,7 +395,7 @@
final ProcessRecord app = processes.get(i);
boolean includeWarmPkg = false;
for (int j = warmServices.size() - 1; j >= 0; j--) {
- if (app.pkgList.containsKey(warmServices.valueAt(j).getPackageName())) {
+ if (app.getPkgList().containsKey(warmServices.valueAt(j).getPackageName())) {
includeWarmPkg = true;
break;
}
@@ -422,7 +423,7 @@
if (oomAdjAll && mConstants.OOMADJ_UPDATE_QUICK) {
return updateOomAdjLocked(app, oomAdjReason);
}
- final ProcessRecord TOP_APP = mService.getTopAppLocked();
+ final ProcessRecord topApp = mService.getTopApp();
final boolean wasCached = app.isCached();
mAdjSeq++;
@@ -435,7 +436,7 @@
? app.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 = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,
+ boolean success = updateOomAdjLocked(app, cachedAdj, topApp, false,
SystemClock.uptimeMillis());
if (oomAdjAll
&& (wasCached != app.isCached() || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
@@ -488,7 +489,7 @@
if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
&& (uidRec.setProcState != uidRec.getCurProcState()
|| uidRec.setCapability != uidRec.curCapability
- || uidRec.setWhitelist != uidRec.curWhitelist)) {
+ || uidRec.mSetAllowlist != uidRec.mCurAllowlist)) {
ActiveUids uids = mTmpUidRecords;
uids.clear();
uids.put(uidRec.uid, uidRec);
@@ -505,7 +506,7 @@
*/
@GuardedBy("mService")
void updateOomAdjLocked(String oomAdjReason) {
- final ProcessRecord topApp = mService.getTopAppLocked();
+ final ProcessRecord topApp = mService.getTopApp();
// Clear any pending ones because we are doing a full update now.
mPendingProcessSet.clear();
mService.mAppProfiler.mHasPreviousProcess = mService.mAppProfiler.mHasHomeProcess = false;
@@ -527,7 +528,7 @@
return true;
}
- final ProcessRecord topApp = mService.getTopAppLocked();
+ final ProcessRecord topApp = mService.getTopApp();
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
mService.mOomAdjProfiler.oomAdjStarted();
@@ -676,7 +677,7 @@
if (mPendingProcessSet.isEmpty()) {
return;
}
- final ProcessRecord topApp = mService.getTopAppLocked();
+ final ProcessRecord topApp = mService.getTopApp();
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
mService.mOomAdjProfiler.oomAdjStarted();
@@ -1112,19 +1113,19 @@
if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
&& (uidRec.setProcState != uidRec.getCurProcState()
|| uidRec.setCapability != uidRec.curCapability
- || uidRec.setWhitelist != uidRec.curWhitelist)) {
+ || uidRec.mSetAllowlist != uidRec.mCurAllowlist)) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
+ ": proc state from " + uidRec.setProcState + " to "
+ uidRec.getCurProcState() + ", capability from "
+ uidRec.setCapability + " to " + uidRec.curCapability
- + ", whitelist from " + uidRec.setWhitelist
- + " to " + uidRec.curWhitelist);
+ + ", allowlist from " + uidRec.mSetAllowlist
+ + " to " + uidRec.mCurAllowlist);
if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
- && !uidRec.curWhitelist) {
+ && !uidRec.mCurAllowlist) {
// UID is now in the background (and not on the temp allowlist). Was it
// previously in the foreground (or on the temp allowlist)?
if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
- || uidRec.setWhitelist) {
+ || uidRec.mSetAllowlist) {
uidRec.lastBackgroundTime = nowElapsed;
if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
// Note: the background settle time is in elapsed realtime, while
@@ -1156,7 +1157,7 @@
}
uidRec.setProcState = uidRec.getCurProcState();
uidRec.setCapability = uidRec.curCapability;
- uidRec.setWhitelist = uidRec.curWhitelist;
+ uidRec.mSetAllowlist = uidRec.mCurAllowlist;
uidRec.setIdle = uidRec.idle;
mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState);
mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
@@ -1353,7 +1354,7 @@
// The max adjustment doesn't allow this app to be anything
// below foreground, so it is not worth doing work for it.
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- mService.reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
}
app.adjType = "fixed";
app.adjSeq = mAdjSeq;
@@ -1379,7 +1380,7 @@
app.systemNoUi = false;
}
if (!app.systemNoUi) {
- if (mService.mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
+ if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE) {
// screen on, promote UI
app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
@@ -1786,8 +1787,9 @@
int clientProcState = client.getCurRawProcState();
// pass client's mAllowStartFgs to the app if client is not persistent process.
- if (client.mAllowStartFgs && client.maxAdj >= ProcessList.FOREGROUND_APP_ADJ) {
- app.mAllowStartFgs = true;
+ if (client.mAllowStartFgs != FGS_FEATURE_DENIED
+ && client.maxAdj >= ProcessList.FOREGROUND_APP_ADJ) {
+ app.mAllowStartFgs = client.mAllowStartFgs;
}
if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
@@ -1912,7 +1914,7 @@
clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
app.bumpAllowStartFgsState(
PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- } else if (mService.mWakefulness
+ } else if (mService.mWakefulness.get()
== PowerManagerInternal.WAKEFULNESS_AWAKE
&& (cr.flags & Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
!= 0) {
@@ -2197,7 +2199,8 @@
// is large we want to force it down since we would prefer to
// keep launcher over it.
if (!mService.mAppProfiler.isLastMemoryLevelNormal()
- && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
+ && app.mProfile.getLastPss()
+ >= mProcessList.getCachedRestoreThresholdKb()) {
app.serviceHighRam = true;
app.serviceb = true;
//Slog.i(TAG, "ADJ " + app + " high ram!");
@@ -2228,7 +2231,7 @@
// Put bound foreground services in a special sched group for additional
// restrictions on screen off
if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- && mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
+ && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE) {
if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) {
schedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
}
@@ -2324,11 +2327,13 @@
}
/** Inform the oomadj observer of changes to oomadj. Used by tests. */
- @GuardedBy("mService")
void reportOomAdjMessageLocked(String tag, String msg) {
Slog.d(tag, msg);
- if (mService.mCurOomAdjObserver != null) {
- mService.mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget();
+ synchronized (mService.mOomAdjObserverLock) {
+ if (mService.mCurOomAdjObserver != null) {
+ mService.mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg)
+ .sendToTarget();
+ }
}
}
@@ -2361,7 +2366,7 @@
&& app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
mCachedAppOptimizer.compactAppFull(app);
}
- } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
+ } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.setAdj < ProcessList.FOREGROUND_APP_ADJ
// Because these can fire independent of oom_adj/procstate changes, we need
// to throttle the actual dispatch of these requests in addition to the
@@ -2369,7 +2374,7 @@
// and in CachedAppOptimizer.
&& mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
mCachedAppOptimizer.compactAppPersistent(app);
- } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
+ } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.getCurProcState()
== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
@@ -2511,18 +2516,22 @@
}
}
}
+ boolean forceUpdatePssTime = false;
if (app.setProcState == PROCESS_STATE_NONEXISTENT
|| ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
app.lastStateTime = now;
- mService.mAppProfiler.updateNextPssTimeLocked(app.getCurProcState(), app, now, true);
+ forceUpdatePssTime = true;
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Process state change from "
+ ProcessList.makeProcStateString(app.setProcState) + " to "
+ ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
- + (app.nextPssTime - now) + ": " + app);
+ + (app.mProfile.getNextPssTime() - now) + ": " + app);
}
- } else {
- mService.mAppProfiler.updateNextPssTimeLocked(app.getCurProcState(), app, now, false);
+ }
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ app.mProfile.updateProcState(app);
+ mService.mAppProfiler.updateNextPssTimeLPf(
+ app.getCurProcState(), app.mProfile, now, forceUpdatePssTime);
}
if (app.setProcState != app.getCurProcState()) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
@@ -2538,7 +2547,7 @@
// arbitrary amounts of battery power. Note its current CPU time to later know to
// kill it if it is not behaving well.
app.setWhenUnimportant(now);
- app.lastCpuTime = 0;
+ app.mProfile.mLastCpuTime.set(0);
}
// Inform UsageStats of important process state change
// Must be called before updating setProcState
@@ -2576,7 +2585,7 @@
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Changes in " + app + ": " + changes);
ActivityManagerService.ProcessChangeItem item =
- mService.enqueueProcessChangeItemLocked(app.pid, app.info.uid);
+ mProcessList.enqueueProcessChangeItemLocked(app.pid, app.info.uid);
item.changes |= changes;
item.foregroundActivities = app.repForegroundActivities;
item.capability = app.setCapability;
@@ -2730,27 +2739,27 @@
}
@GuardedBy("mService")
- final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) {
+ void setAppIdTempAllowlistStateLocked(int uid, boolean onAllowlist) {
boolean changed = false;
for (int i = mActiveUids.size() - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
- if (uidRec.uid == uid && uidRec.curWhitelist != onWhitelist) {
- uidRec.curWhitelist = onWhitelist;
+ if (uidRec.uid == uid && uidRec.mCurAllowlist != onAllowlist) {
+ uidRec.mCurAllowlist = onAllowlist;
changed = true;
}
}
if (changed) {
- updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST);
+ updateOomAdjLocked(OOM_ADJ_REASON_ALLOWLIST);
}
}
@GuardedBy("mService")
- final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
+ void setUidTempAllowlistStateLocked(int uid, boolean onAllowlist) {
boolean changed = false;
final UidRecord uidRec = mActiveUids.get(uid);
- if (uidRec != null && uidRec.curWhitelist != onWhitelist) {
- uidRec.curWhitelist = onWhitelist;
- updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST);
+ if (uidRec != null && uidRec.mCurAllowlist != onAllowlist) {
+ uidRec.mCurAllowlist = onAllowlist;
+ updateOomAdjLocked(OOM_ADJ_REASON_ALLOWLIST);
}
}
diff --git a/services/core/java/com/android/server/am/PackageList.java b/services/core/java/com/android/server/am/PackageList.java
new file mode 100644
index 0000000..978bcb7
--- /dev/null
+++ b/services/core/java/com/android/server/am/PackageList.java
@@ -0,0 +1,155 @@
+/*
+ * 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.am;
+
+import android.content.pm.VersionedPackage;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.procstats.ProcessStats;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * List of packages running in the process, self locked.
+ */
+final class PackageList {
+ private final ProcessRecord mProcess;
+
+ private final ArrayMap<String, ProcessStats.ProcessStateHolder> mPkgList = new ArrayMap<>();
+
+ PackageList(final ProcessRecord app) {
+ mProcess = app;
+ }
+
+ ProcessStats.ProcessStateHolder put(String key, ProcessStats.ProcessStateHolder value) {
+ synchronized (this) {
+ mProcess.getWindowProcessController().addPackage(key);
+ return mPkgList.put(key, value);
+ }
+ }
+
+ void clear() {
+ synchronized (this) {
+ mPkgList.clear();
+ mProcess.getWindowProcessController().clearPackageList();
+ }
+ }
+
+ int size() {
+ synchronized (this) {
+ return mPkgList.size();
+ }
+ }
+
+ boolean containsKey(Object key) {
+ synchronized (this) {
+ return mPkgList.containsKey(key);
+ }
+ }
+
+ ProcessStats.ProcessStateHolder get(String pkgName) {
+ synchronized (this) {
+ return mPkgList.get(pkgName);
+ }
+ }
+
+ void forEachPackage(Consumer<String> callback) {
+ synchronized (this) {
+ for (int i = 0, size = mPkgList.size(); i < size; i++) {
+ callback.accept(mPkgList.keyAt(i));
+ }
+ }
+ }
+
+ void forEachPackage(BiConsumer<String, ProcessStats.ProcessStateHolder> callback) {
+ synchronized (this) {
+ for (int i = 0, size = mPkgList.size(); i < size; i++) {
+ callback.accept(mPkgList.keyAt(i), mPkgList.valueAt(i));
+ }
+ }
+ }
+
+ <R> R forEachPackage(Function<String, R> callback) {
+ synchronized (this) {
+ for (int i = 0, size = mPkgList.size(); i < size; i++) {
+ R r = callback.apply(mPkgList.keyAt(i));
+ if (r != null) {
+ return r;
+ }
+ }
+ }
+ return null;
+ }
+
+ void forEachPackageProcessStats(Consumer<ProcessStats.ProcessStateHolder> callback) {
+ synchronized (this) {
+ for (int i = 0, size = mPkgList.size(); i < size; i++) {
+ callback.accept(mPkgList.valueAt(i));
+ }
+ }
+ }
+
+ @GuardedBy("this")
+ ArrayMap<String, ProcessStats.ProcessStateHolder> getPackageListLocked() {
+ return mPkgList;
+ }
+
+ String[] getPackageList() {
+ synchronized (this) {
+ int size = mPkgList.size();
+ if (size == 0) {
+ return null;
+ }
+ final String[] list = new String[size];
+ for (int i = 0; i < size; i++) {
+ list[i] = mPkgList.keyAt(i);
+ }
+ return list;
+ }
+ }
+
+ List<VersionedPackage> getPackageListWithVersionCode() {
+ synchronized (this) {
+ int size = mPkgList.size();
+ if (size == 0) {
+ return null;
+ }
+ List<VersionedPackage> list = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ list.add(new VersionedPackage(mPkgList.keyAt(i), mPkgList.valueAt(i).appVersion));
+ }
+ return list;
+ }
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ synchronized (this) {
+ pw.print(prefix); pw.print("packageList={");
+ for (int i = 0, size = mPkgList.size(); i < size; i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(mPkgList.keyAt(i));
+ }
+ pw.println("}");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 631b632..0eb48f6 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -24,7 +24,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
@@ -65,7 +64,8 @@
boolean canceled = false;
/**
* Map IBinder to duration specified as Pair<Long, Integer>, Long is allowlist duration in
- * milliseconds, Integer is allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * milliseconds, Integer is allowlist type defined at
+ * {@link android.os.PowerWhitelistManager.TempAllowListType}
*/
private ArrayMap<IBinder, Pair<Long, Integer>> mWhitelistDuration;
private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
diff --git a/services/core/java/com/android/server/am/PendingTempAllowlists.java b/services/core/java/com/android/server/am/PendingTempAllowlists.java
new file mode 100644
index 0000000..75935c4
--- /dev/null
+++ b/services/core/java/com/android/server/am/PendingTempAllowlists.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.util.SparseArray;
+
+/** Allowlists of uids to temporarily bypass Power Save mode. */
+final class PendingTempAllowlists {
+
+ private ActivityManagerService mService;
+
+ private final SparseArray<ActivityManagerService.PendingTempAllowlist> mPendingTempAllowlist =
+ new SparseArray<>();
+
+ PendingTempAllowlists(ActivityManagerService service) {
+ mService = service;
+ }
+
+ void put(int uid, ActivityManagerService.PendingTempAllowlist value) {
+ mPendingTempAllowlist.put(uid, value);
+ mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag);
+ }
+
+ void removeAt(int index) {
+ final int uid = mPendingTempAllowlist.keyAt(index);
+ mPendingTempAllowlist.removeAt(index);
+ mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid);
+ }
+
+ ActivityManagerService.PendingTempAllowlist get(int uid) {
+ return mPendingTempAllowlist.get(uid);
+ }
+
+ int size() {
+ return mPendingTempAllowlist.size();
+ }
+
+ ActivityManagerService.PendingTempAllowlist valueAt(int index) {
+ return mPendingTempAllowlist.valueAt(index);
+ }
+
+ int indexOfKey(int key) {
+ return mPendingTempAllowlist.indexOfKey(key);
+ }
+}
diff --git a/services/core/java/com/android/server/am/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempWhitelists.java
deleted file mode 100644
index 50d58f0..0000000
--- a/services/core/java/com/android/server/am/PendingTempWhitelists.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.am;
-
-import android.util.SparseArray;
-
-/** Whitelists of uids to temporarily bypass Power Save mode. */
-final class PendingTempWhitelists {
-
- private ActivityManagerService mService;
-
- private final SparseArray<ActivityManagerService.PendingTempWhitelist> mPendingTempWhitelist =
- new SparseArray<>();
-
- PendingTempWhitelists(ActivityManagerService service) {
- mService = service;
- }
-
- void put(int uid, ActivityManagerService.PendingTempWhitelist value) {
- mPendingTempWhitelist.put(uid, value);
- mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag);
- }
-
- void removeAt(int index) {
- final int uid = mPendingTempWhitelist.keyAt(index);
- mPendingTempWhitelist.removeAt(index);
- mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid);
- }
-
- ActivityManagerService.PendingTempWhitelist get(int uid) {
- return mPendingTempWhitelist.get(uid);
- }
-
- int size() {
- return mPendingTempWhitelist.size();
- }
-
- ActivityManagerService.PendingTempWhitelist valueAt(int index) {
- return mPendingTempWhitelist.valueAt(index);
- }
-
- int indexOfKey(int key) {
- return mPendingTempWhitelist.indexOfKey(key);
- }
-}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 2273779..8f99459 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -34,10 +34,14 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESSES_CHANGED_UI_MSG;
+import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESS_DIED_UI_MSG;
import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_DELAY_MS;
import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_MSG;
import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK;
@@ -59,6 +63,7 @@
import android.app.ApplicationExitInfo.Reason;
import android.app.ApplicationExitInfo.SubReason;
import android.app.IApplicationThread;
+import android.app.IProcessObserver;
import android.app.IUidObserver;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
@@ -86,6 +91,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -97,18 +103,20 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.DebugUtils;
import android.util.EventLog;
import android.util.LongSparseArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
-import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -117,6 +125,7 @@
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService.ProcessChangeItem;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -134,6 +143,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -145,6 +156,8 @@
public final class ProcessList {
static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
+ static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
+
// A system property to control if app data isolation is enabled.
static final String ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.zygote.app_data_isolation";
@@ -416,7 +429,7 @@
private boolean mVoldAppDataIsolationEnabled = false;
- private ArrayList<String> mAppDataIsolationWhitelistedApps;
+ private ArrayList<String> mAppDataIsolationAllowlistedApps;
/**
* Temporary to avoid allocations. Protected by main lock.
@@ -643,6 +656,25 @@
*/
final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
+ // Self locked with the inner lock within the RemoteCallbackList
+ private final RemoteCallbackList<IProcessObserver> mProcessObservers =
+ new RemoteCallbackList<>();
+
+ // No lock is needed as it's accessed from single thread only
+ private ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
+
+ @GuardedBy("mProcessChangeLock")
+ private final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>();
+
+ @GuardedBy("mProcessChangeLock")
+ final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>();
+
+ /**
+ * A dedicated lock for dispatching the process changes as it occurs frequently
+ */
+ private final Object mProcessChangeLock = new Object();
+
+
/**
* All of the applications we currently have running organized by name.
* The keys are strings of the application package name (as
@@ -723,7 +755,7 @@
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
- mAppDataIsolationWhitelistedApps = new ArrayList<>(
+ mAppDataIsolationAllowlistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
if (sKillHandler == null) {
@@ -1523,47 +1555,34 @@
ProcessRecord proc = mProcessNames.get(processName, uid);
if (false && proc != null && !keepIfLarge
&& proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
- && proc.lastCachedPss >= 4000) {
+ && proc.mProfile.getLastCachedPss() >= 4000) {
// Turn this condition on to cause killing to happen regularly, for testing.
- synchronized (mService.mProcessStats.mLock) {
- if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(
- proc.pkgList.mPkgList, proc.lastCachedPss);
- for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
- FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- proc.lastCachedPss, holder.appVersion);
- }
- }
+ final long lastCachedPss;
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ proc.mProfile.reportCachedKill();
+ lastCachedPss = proc.mProfile.getLastCachedPss();
}
- proc.kill(Long.toString(proc.lastCachedPss) + "k from cached",
+ proc.kill(Long.toString(lastCachedPss) + "k from cached",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_LARGE_CACHED,
true);
} else if (proc != null && !keepIfLarge
&& !mService.mAppProfiler.isLastMemoryLevelNormal()
&& proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
- if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc
- .lastCachedPss);
- if (proc.lastCachedPss >= getCachedRestoreThresholdKb()) {
- synchronized (mService.mProcessStats.mLock) {
- if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList,
- proc.lastCachedPss);
- for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
- FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- proc.lastCachedPss, holder.appVersion);
- }
- }
+ final long lastCachedPss;
+ boolean doKilling = false;
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ lastCachedPss = proc.mProfile.getLastCachedPss();
+ if (lastCachedPss >= getCachedRestoreThresholdKb()) {
+ proc.mProfile.reportCachedKill();
+ doKilling = true;
}
- proc.kill(Long.toString(proc.lastCachedPss) + "k from cached",
+ }
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + lastCachedPss);
+ }
+ if (doKilling) {
+ proc.kill(Long.toString(lastCachedPss) + "k from cached",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_LARGE_CACHED,
true);
@@ -1748,8 +1767,7 @@
*/
@GuardedBy("mService")
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
- int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
- String abiOverride) {
+ int zygotePolicyFlags, boolean disableHiddenApiChecks, String abiOverride) {
if (app.pendingStart) {
return true;
}
@@ -1770,7 +1788,7 @@
mService.mProcessesOnHold.remove(app);
checkSlow(startTime, "startProcess: starting to update cpu stats");
- mService.updateCpuStatsLocked();
+ mService.updateCpuStats();
checkSlow(startTime, "startProcess: done updating cpu stats");
try {
@@ -1873,10 +1891,16 @@
mService.mNativeDebuggingApp = null;
}
- if (app.info.isEmbeddedDexUsed()
- || (app.info.isPrivilegedApp()
- && DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet()))) {
+ if (app.info.isEmbeddedDexUsed()) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
+ } else if (app.info.isPrivilegedApp()) {
+ final PackageList pkgList = app.getPkgList();
+ synchronized (pkgList) {
+ if (DexManager.isPackageSelectedToRunOob(
+ pkgList.getPackageListLocked().keySet())) {
+ runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
+ }
+ }
}
if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {
@@ -1889,10 +1913,6 @@
throw new IllegalStateException("Invalid API policy: " + policy);
}
runtimeFlags |= policyBits;
-
- if (disableTestApiChecks) {
- runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;
- }
}
String useAppImageCache = SystemProperties.get(
@@ -2240,7 +2260,7 @@
}
Map<String, Pair<String, Long>> pkgDataInfoMap;
- Map<String, Pair<String, Long>> whitelistedAppDataInfoMap;
+ Map<String, Pair<String, Long>> allowlistedAppDataInfoMap;
boolean bindMountAppStorageDirs = false;
boolean bindMountAppsData = mAppDataIsolationEnabled
&& (UserHandle.isApp(app.uid) || UserHandle.isIsolated(app.uid))
@@ -2248,7 +2268,7 @@
// Get all packages belongs to the same shared uid. sharedPackages is empty array
// if it doesn't have shared uid.
- final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
+ final PackageManagerInternal pmInt = mService.getPackageManagerInternal();
final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
app.info.packageName, app.userId);
final String[] targetPackagesList = sharedPackages.length == 0
@@ -2261,16 +2281,16 @@
bindMountAppsData = false;
}
- // Remove all packages in pkgDataInfoMap from mAppDataIsolationWhitelistedApps, so
+ // Remove all packages in pkgDataInfoMap from mAppDataIsolationAllowlistedApps, so
// it won't be mounted twice.
- final Set<String> whitelistedApps = new ArraySet<>(mAppDataIsolationWhitelistedApps);
+ final Set<String> allowlistedApps = new ArraySet<>(mAppDataIsolationAllowlistedApps);
for (String pkg : targetPackagesList) {
- whitelistedApps.remove(pkg);
+ allowlistedApps.remove(pkg);
}
- whitelistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt,
- whitelistedApps.toArray(new String[0]), uid);
- if (whitelistedAppDataInfoMap == null) {
+ allowlistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt,
+ allowlistedApps.toArray(new String[0]), uid);
+ if (allowlistedAppDataInfoMap == null) {
// TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
// tmp free pass.
bindMountAppsData = false;
@@ -2295,7 +2315,7 @@
// since it has no access to them anyway.
if (app.isolated) {
pkgDataInfoMap = null;
- whitelistedAppDataInfoMap = null;
+ allowlistedAppDataInfoMap = null;
}
final Process.ProcessStartResult startResult;
@@ -2314,7 +2334,7 @@
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
- app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
+ app.mDisabledCompatChanges, pkgDataInfoMap, allowlistedAppDataInfoMap,
false, false,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else {
@@ -2323,7 +2343,7 @@
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
- whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
+ allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
}
checkSlow(startTime, "startProcess: returned from zygote!");
@@ -2342,8 +2362,7 @@
final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
int zygotePolicyFlags, String abiOverride) {
return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
- false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
- abiOverride);
+ false /* disableHiddenApiChecks */, abiOverride);
}
@GuardedBy("mService")
@@ -2373,7 +2392,7 @@
// if it had been bad.
if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+ "/" + processName);
- mService.mAppErrors.resetProcessCrashTimeLocked(processName, info.uid);
+ mService.mAppErrors.resetProcessCrashTime(processName, info.uid);
if (mService.mAppErrors.isBadProcess(processName, info.uid)) {
EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
UserHandle.getUserId(info.uid), info.uid,
@@ -2421,7 +2440,7 @@
if (!app.killed
|| mService.mAppProfiler.isLastMemoryLevelNormal()
|| app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
- || app.lastCachedPss < getCachedRestoreThresholdKb()) {
+ || app.mProfile.getLastCachedPss() < getCachedRestoreThresholdKb()) {
// Throw a wtf if it's not killed, or killed but not because the system was in
// memory pressure + the app was in "cch-empty" and used large amount of memory
Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
@@ -2736,7 +2755,7 @@
if (userId != UserHandle.USER_ALL && app.userId != userId) {
continue;
}
- if (!app.pkgList.containsKey(packageName) && !isDep) {
+ if (!app.getPkgList().containsKey(packageName) && !isDep) {
continue;
}
}
@@ -2797,7 +2816,7 @@
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
- mService.getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
+ mService.getPackageManagerInternal().removeIsolatedUid(app.uid);
}
}
boolean willRestart = false;
@@ -2847,10 +2866,10 @@
// This is the first appearance of the uid, report it now!
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Creating new process uid: " + uidRec);
- if (Arrays.binarySearch(mService.mDeviceIdleTempWhitelist,
+ if (Arrays.binarySearch(mService.mDeviceIdleTempAllowlist,
UserHandle.getAppId(proc.uid)) >= 0
- || mService.mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
- uidRec.setWhitelist = uidRec.curWhitelist = true;
+ || mService.mPendingTempAllowlist.indexOfKey(proc.uid) >= 0) {
+ uidRec.mSetAllowlist = uidRec.mCurAllowlist = true;
}
uidRec.updateHasInternetPermission();
mActiveUids.put(proc.uid, uidRec);
@@ -2904,7 +2923,7 @@
uid = isolatedUid;
}
mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(uid, info.uid);
- mService.getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid);
+ mService.getPackageManagerInternal().addIsolatedUid(uid, info.uid);
// Register the isolated UID with this application so BatteryStats knows to
// attribute resource usage to the application.
@@ -2964,6 +2983,7 @@
UidRecord.CHANGE_GONE);
EventLogTags.writeAmUidStopped(uid);
mActiveUids.remove(uid);
+ mService.mFgsStartTempAllowList.remove(record.info.uid);
mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,
ActivityManager.PROCESS_CAPABILITY_NONE);
}
@@ -3582,14 +3602,14 @@
if (app.hasActivities()) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
}
- outInfo.lastTrimLevel = app.trimMemoryLevel;
+ outInfo.lastTrimLevel = app.mProfile.getTrimMemoryLevel();
int adj = app.curAdj;
int procState = app.getCurProcState();
outInfo.importance = procStateToImportance(procState, adj, outInfo,
clientTargetSdk);
outInfo.importanceReasonCode = app.adjTypeCode;
outInfo.processState = app.getCurProcState();
- outInfo.isFocused = (app == mService.getTopAppLocked());
+ outInfo.isFocused = (app == mService.getTopApp());
outInfo.lastActivityTime = app.lastActivityTime;
}
@@ -3653,6 +3673,729 @@
pw.println("):");
}
+ void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc, String prefix) {
+ pw.print(prefix);
+ pw.print('#');
+ if (index < 10) {
+ pw.print(' ');
+ }
+ pw.print(index);
+ pw.print(": ");
+ pw.print(makeOomAdjString(proc.setAdj, false));
+ pw.print(' ');
+ pw.print(makeProcStateString(proc.getCurProcState()));
+ pw.print(' ');
+ ActivityManager.printCapabilitiesSummary(pw, proc.curCapability);
+ pw.print(' ');
+ pw.print(proc.toShortString());
+ if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities()
+ || proc.treatLikeActivity) {
+ pw.print(" act:");
+ boolean printed = false;
+ if (proc.hasActivities()) {
+ pw.print("activities");
+ printed = true;
+ }
+ if (proc.hasRecentTasks()) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("recents");
+ printed = true;
+ }
+ if (proc.hasClientActivities()) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("client");
+ printed = true;
+ }
+ if (proc.treatLikeActivity) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("treated");
+ }
+ }
+ pw.println();
+ }
+
+ boolean dumpLruLocked(PrintWriter pw, String dumpPackage, String prefix) {
+ final int lruSize = mLruProcesses.size();
+ final String innerPrefix;
+ if (prefix == null) {
+ pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)");
+ innerPrefix = " ";
+ } else {
+ boolean haveAny = false;
+ for (int i = lruSize - 1; i >= 0; i--) {
+ final ProcessRecord r = mLruProcesses.get(i);
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
+ continue;
+ }
+ haveAny = true;
+ break;
+ }
+ if (!haveAny) {
+ return false;
+ }
+ pw.print(prefix);
+ pw.println("Raw LRU list (dumpsys activity lru):");
+ innerPrefix = prefix + " ";
+ }
+ int i;
+ boolean first = true;
+ for (i = lruSize - 1; i >= mLruProcessActivityStart; i--) {
+ final ProcessRecord r = mLruProcesses.get(i);
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.print(innerPrefix);
+ pw.println("Activities:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r, innerPrefix);
+ }
+ first = true;
+ for (; i >= mLruProcessServiceStart; i--) {
+ final ProcessRecord r = mLruProcesses.get(i);
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.print(innerPrefix);
+ pw.println("Services:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r, innerPrefix);
+ }
+ first = true;
+ for (; i >= 0; i--) {
+ final ProcessRecord r = mLruProcesses.get(i);
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.print(innerPrefix);
+ pw.println("Other:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r, innerPrefix);
+ }
+ return true;
+ }
+
+ @GuardedBy("mService")
+ void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
+ boolean needSep = false;
+ int numPers = 0;
+
+ pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
+
+ if (dumpAll || dumpPackage != null) {
+ final int numOfNames = mProcessNames.getMap().size();
+ for (int ip = 0; ip < numOfNames; ip++) {
+ SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+ for (int ia = 0, size = procs.size(); ia < size; ia++) {
+ ProcessRecord r = procs.valueAt(ia);
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
+ continue;
+ }
+ if (!needSep) {
+ pw.println(" All known processes:");
+ needSep = true;
+ }
+ pw.print(r.isPersistent() ? " *PERS*" : " *APP*");
+ pw.print(" UID "); pw.print(procs.keyAt(ia));
+ pw.print(" "); pw.println(r);
+ r.dump(pw, " ");
+ if (r.isPersistent()) {
+ numPers++;
+ }
+ }
+ }
+ }
+
+ if (mIsolatedProcesses.size() > 0) {
+ boolean printed = false;
+ for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) {
+ ProcessRecord r = mIsolatedProcesses.valueAt(i);
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" Isolated process list (sorted by uid):");
+ printed = true;
+ needSep = true;
+ }
+ pw.print(" Isolated #"); pw.print(i); pw.print(": ");
+ pw.println(r);
+ }
+ }
+
+ needSep = mService.dumpActiveInstruments(pw, dumpPackage, needSep);
+
+ if (dumpOomLocked(fd, pw, needSep, args, opti, dumpAll, dumpPackage, false)) {
+ needSep = true;
+ }
+
+ if (mActiveUids.size() > 0) {
+ needSep |= mActiveUids.dump(pw, dumpPackage, dumpAppId,
+ "UID states:", needSep);
+ }
+
+ if (dumpAll) {
+ needSep |= mService.mUidObserverController.dumpValidateUids(pw,
+ dumpPackage, dumpAppId, "UID validation:", needSep);
+ }
+
+ if (needSep) {
+ pw.println();
+ }
+ if (dumpLruLocked(pw, dumpPackage, " ")) {
+ needSep = true;
+ }
+
+ if (getLruSizeLocked() > 0) {
+ if (needSep) {
+ pw.println();
+ }
+ dumpLruListHeaderLocked(pw);
+ dumpProcessOomList(pw, mService, mLruProcesses, " ", "Proc", "PERS", false,
+ dumpPackage);
+ needSep = true;
+ }
+
+ mService.dumpOtherProcessesInfoLocked(fd, pw, dumpAll, dumpPackage, dumpAppId, numPers,
+ needSep);
+ }
+
+ @GuardedBy("this")
+ void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
+ int numPers = 0;
+
+ final int numOfNames = mProcessNames.getMap().size();
+ for (int ip = 0; ip < numOfNames; ip++) {
+ SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+ for (int ia = 0, size = procs.size(); ia < size; ia++) {
+ ProcessRecord r = procs.valueAt(ia);
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
+ continue;
+ }
+ r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.PROCS,
+ mLruProcesses.indexOf(r)
+ );
+ if (r.isPersistent()) {
+ numPers++;
+ }
+ }
+ }
+
+ for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) {
+ ProcessRecord r = mIsolatedProcesses.valueAt(i);
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
+ continue;
+ }
+ r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS,
+ mLruProcesses.indexOf(r)
+ );
+ }
+
+ final int dumpAppId = mService.getAppId(dumpPackage);
+ mActiveUids.dumpProto(proto, dumpPackage, dumpAppId,
+ ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
+
+ if (getLruSizeLocked() > 0) {
+ long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
+ int total = getLruSizeLocked();
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT,
+ total - mLruProcessActivityStart);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT,
+ total - mLruProcessServiceStart);
+ writeProcessOomListToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, mService,
+ mLruProcesses, false, dumpPackage);
+ proto.end(lruToken);
+ }
+
+ mService.writeOtherProcessesInfoToProtoLocked(proto, dumpPackage, dumpAppId, numPers);
+ }
+
+ private static ArrayList<Pair<ProcessRecord, Integer>> sortProcessOomList(
+ List<ProcessRecord> origList, String dumpPackage) {
+ ArrayList<Pair<ProcessRecord, Integer>> list =
+ new ArrayList<Pair<ProcessRecord, Integer>>(origList.size());
+ for (int i = 0, size = origList.size(); i < size; i++) {
+ ProcessRecord r = origList.get(i);
+ if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
+ continue;
+ }
+ list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i));
+ }
+
+ Comparator<Pair<ProcessRecord, Integer>> comparator =
+ new Comparator<Pair<ProcessRecord, Integer>>() {
+ @Override
+ public int compare(Pair<ProcessRecord, Integer> object1,
+ Pair<ProcessRecord, Integer> object2) {
+ if (object1.first.setAdj != object2.first.setAdj) {
+ return object1.first.setAdj > object2.first.setAdj ? -1 : 1;
+ }
+ if (object1.first.setProcState != object2.first.setProcState) {
+ return object1.first.setProcState > object2.first.setProcState ? -1 : 1;
+ }
+ if (object1.second.intValue() != object2.second.intValue()) {
+ return object1.second.intValue() > object2.second.intValue() ? -1 : 1;
+ }
+ return 0;
+ }
+ };
+
+ Collections.sort(list, comparator);
+ return list;
+ }
+
+ private static boolean writeProcessOomListToProto(ProtoOutputStream proto, long fieldId,
+ ActivityManagerService service, List<ProcessRecord> origList,
+ boolean inclDetails, String dumpPackage) {
+ ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage);
+ if (list.isEmpty()) return false;
+
+ final long curUptime = SystemClock.uptimeMillis();
+
+ for (int i = list.size() - 1; i >= 0; i--) {
+ ProcessRecord r = list.get(i).first;
+ long token = proto.start(fieldId);
+ String oomAdj = makeOomAdjString(r.setAdj, true);
+ proto.write(ProcessOomProto.PERSISTENT, r.isPersistent());
+ proto.write(ProcessOomProto.NUM, (origList.size() - 1) - list.get(i).second);
+ proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
+ int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
+ switch (r.setSchedGroup) {
+ case SCHED_GROUP_BACKGROUND:
+ schedGroup = ProcessOomProto.SCHED_GROUP_BACKGROUND;
+ break;
+ case SCHED_GROUP_DEFAULT:
+ schedGroup = ProcessOomProto.SCHED_GROUP_DEFAULT;
+ break;
+ case SCHED_GROUP_TOP_APP:
+ schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP;
+ break;
+ case SCHED_GROUP_TOP_APP_BOUND:
+ schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP_BOUND;
+ break;
+ }
+ if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) {
+ proto.write(ProcessOomProto.SCHED_GROUP, schedGroup);
+ }
+ if (r.hasForegroundActivities()) {
+ proto.write(ProcessOomProto.ACTIVITIES, true);
+ } else if (r.hasForegroundServices()) {
+ proto.write(ProcessOomProto.SERVICES, true);
+ }
+ proto.write(ProcessOomProto.STATE,
+ makeProcStateProtoEnum(r.getCurProcState()));
+ proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.mProfile.getTrimMemoryLevel());
+ r.dumpDebug(proto, ProcessOomProto.PROC);
+ proto.write(ProcessOomProto.ADJ_TYPE, r.adjType);
+ if (r.adjSource != null || r.adjTarget != null) {
+ if (r.adjTarget instanceof ComponentName) {
+ ComponentName cn = (ComponentName) r.adjTarget;
+ cn.dumpDebug(proto, ProcessOomProto.ADJ_TARGET_COMPONENT_NAME);
+ } else if (r.adjTarget != null) {
+ proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, r.adjTarget.toString());
+ }
+ if (r.adjSource instanceof ProcessRecord) {
+ ProcessRecord p = (ProcessRecord) r.adjSource;
+ p.dumpDebug(proto, ProcessOomProto.ADJ_SOURCE_PROC);
+ } else if (r.adjSource != null) {
+ proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, r.adjSource.toString());
+ }
+ }
+ if (inclDetails) {
+ long detailToken = proto.start(ProcessOomProto.DETAIL);
+ proto.write(ProcessOomProto.Detail.MAX_ADJ, r.maxAdj);
+ proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.getCurRawAdj());
+ proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, r.setRawAdj);
+ proto.write(ProcessOomProto.Detail.CUR_ADJ, r.curAdj);
+ proto.write(ProcessOomProto.Detail.SET_ADJ, r.setAdj);
+ proto.write(ProcessOomProto.Detail.CURRENT_STATE,
+ makeProcStateProtoEnum(r.getCurProcState()));
+ proto.write(ProcessOomProto.Detail.SET_STATE,
+ makeProcStateProtoEnum(r.setProcState));
+ proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString(
+ r.mProfile.getLastPss() * 1024, new StringBuilder()));
+ proto.write(ProcessOomProto.Detail.LAST_SWAP_PSS, DebugUtils.sizeValueToString(
+ r.mProfile.getLastSwapPss() * 1024, new StringBuilder()));
+ proto.write(ProcessOomProto.Detail.LAST_CACHED_PSS, DebugUtils.sizeValueToString(
+ r.mProfile.getLastCachedPss() * 1024, new StringBuilder()));
+ proto.write(ProcessOomProto.Detail.CACHED, r.isCached());
+ proto.write(ProcessOomProto.Detail.EMPTY, r.empty);
+ proto.write(ProcessOomProto.Detail.HAS_ABOVE_CLIENT, r.hasAboveClient);
+
+ if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
+ long lastCpuTime = r.mProfile.mLastCpuTime.get();
+ if (lastCpuTime != 0) {
+ long uptimeSince = curUptime - service.mLastPowerCheckUptime;
+ long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime;
+ long cpuTimeToken = proto.start(ProcessOomProto.Detail.SERVICE_RUN_TIME);
+ proto.write(ProcessOomProto.Detail.CpuRunTime.OVER_MS, uptimeSince);
+ proto.write(ProcessOomProto.Detail.CpuRunTime.USED_MS, timeUsed);
+ proto.write(ProcessOomProto.Detail.CpuRunTime.ULTILIZATION,
+ (100.0 * timeUsed) / uptimeSince);
+ proto.end(cpuTimeToken);
+ }
+ }
+ proto.end(detailToken);
+ }
+ proto.end(token);
+ }
+
+ return true;
+ }
+
+ private static boolean dumpProcessOomList(PrintWriter pw,
+ ActivityManagerService service, List<ProcessRecord> origList,
+ String prefix, String normalLabel, String persistentLabel,
+ boolean inclDetails, String dumpPackage) {
+
+ ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage);
+ if (list.isEmpty()) return false;
+
+ final long curUptime = SystemClock.uptimeMillis();
+ final long uptimeSince = curUptime - service.mLastPowerCheckUptime;
+
+ for (int i = list.size() - 1; i >= 0; i--) {
+ ProcessRecord r = list.get(i).first;
+ String oomAdj = makeOomAdjString(r.setAdj, false);
+ char schedGroup;
+ switch (r.setSchedGroup) {
+ case SCHED_GROUP_BACKGROUND:
+ schedGroup = 'b';
+ break;
+ case SCHED_GROUP_DEFAULT:
+ schedGroup = 'F';
+ break;
+ case SCHED_GROUP_TOP_APP:
+ schedGroup = 'T';
+ break;
+ case SCHED_GROUP_RESTRICTED:
+ schedGroup = 'R';
+ break;
+ case SCHED_GROUP_TOP_APP_BOUND:
+ schedGroup = 'B';
+ break;
+ default:
+ schedGroup = '?';
+ break;
+ }
+ char foreground;
+ if (r.hasForegroundActivities()) {
+ foreground = 'A';
+ } else if (r.hasForegroundServices()) {
+ foreground = 'S';
+ } else {
+ foreground = ' ';
+ }
+ String procState = makeProcStateString(r.getCurProcState());
+ pw.print(prefix);
+ pw.print(r.isPersistent() ? persistentLabel : normalLabel);
+ pw.print(" #");
+ int num = (origList.size() - 1) - list.get(i).second;
+ if (num < 10) pw.print(' ');
+ pw.print(num);
+ pw.print(": ");
+ pw.print(oomAdj);
+ pw.print(' ');
+ pw.print(schedGroup);
+ pw.print('/');
+ pw.print(foreground);
+ pw.print('/');
+ pw.print(procState);
+ pw.print(' ');
+ ActivityManager.printCapabilitiesSummary(pw, r.curCapability);
+ pw.print(' ');
+ pw.print(" t:");
+ if (r.mProfile.getTrimMemoryLevel() < 10) pw.print(' ');
+ pw.print(r.mProfile.getTrimMemoryLevel());
+ pw.print(' ');
+ pw.print(r.toShortString());
+ pw.print(" (");
+ pw.print(r.adjType);
+ pw.println(')');
+ if (r.adjSource != null || r.adjTarget != null) {
+ pw.print(prefix);
+ pw.print(" ");
+ if (r.adjTarget instanceof ComponentName) {
+ pw.print(((ComponentName) r.adjTarget).flattenToShortString());
+ } else if (r.adjTarget != null) {
+ pw.print(r.adjTarget.toString());
+ } else {
+ pw.print("{null}");
+ }
+ pw.print("<=");
+ if (r.adjSource instanceof ProcessRecord) {
+ pw.print("Proc{");
+ pw.print(((ProcessRecord) r.adjSource).toShortString());
+ pw.println("}");
+ } else if (r.adjSource != null) {
+ pw.println(r.adjSource.toString());
+ } else {
+ pw.println("{null}");
+ }
+ }
+ if (inclDetails) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("oom: max="); pw.print(r.maxAdj);
+ pw.print(" curRaw="); pw.print(r.getCurRawAdj());
+ pw.print(" setRaw="); pw.print(r.setRawAdj);
+ pw.print(" cur="); pw.print(r.curAdj);
+ pw.print(" set="); pw.println(r.setAdj);
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("state: cur="); pw.print(
+ makeProcStateString(r.getCurProcState()));
+ pw.print(" set="); pw.print(makeProcStateString(r.setProcState));
+ pw.print(" lastPss=");
+ DebugUtils.printSizeValue(pw, r.mProfile.getLastPss() * 1024);
+ pw.print(" lastSwapPss=");
+ DebugUtils.printSizeValue(pw, r.mProfile.getLastSwapPss() * 1024);
+ pw.print(" lastCachedPss=");
+ DebugUtils.printSizeValue(pw, r.mProfile.getLastCachedPss() * 1024);
+ pw.println();
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("cached="); pw.print(r.isCached());
+ pw.print(" empty="); pw.print(r.empty);
+ pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
+
+ if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
+ long lastCpuTime = r.mProfile.mLastCpuTime.get();
+ if (lastCpuTime != 0) {
+ long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime;
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("run cpu over ");
+ TimeUtils.formatDuration(uptimeSince, pw);
+ pw.print(" used ");
+ TimeUtils.formatDuration(timeUsed, pw);
+ pw.print(" (");
+ pw.print((timeUsed * 100) / uptimeSince);
+ pw.println("%)");
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private void printOomLevel(PrintWriter pw, String name, int adj) {
+ pw.print(" ");
+ if (adj >= 0) {
+ pw.print(' ');
+ if (adj < 10) pw.print(' ');
+ } else {
+ if (adj > -10) pw.print(' ');
+ }
+ pw.print(adj);
+ pw.print(": ");
+ pw.print(name);
+ pw.print(" (");
+ pw.print(ActivityManagerService.stringifySize(getMemLevel(adj), 1024));
+ pw.println(")");
+ }
+
+ boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String[] args,
+ int opti, boolean dumpAll, String dumpPackage, boolean inclGc) {
+ if (getLruSizeLocked() > 0) {
+ if (needSep) pw.println();
+ needSep = true;
+ pw.println(" OOM levels:");
+ printOomLevel(pw, "SYSTEM_ADJ", SYSTEM_ADJ);
+ printOomLevel(pw, "PERSISTENT_PROC_ADJ", PERSISTENT_PROC_ADJ);
+ printOomLevel(pw, "PERSISTENT_SERVICE_ADJ", PERSISTENT_SERVICE_ADJ);
+ printOomLevel(pw, "FOREGROUND_APP_ADJ", FOREGROUND_APP_ADJ);
+ printOomLevel(pw, "VISIBLE_APP_ADJ", VISIBLE_APP_ADJ);
+ printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", PERCEPTIBLE_APP_ADJ);
+ printOomLevel(pw, "PERCEPTIBLE_LOW_APP_ADJ", PERCEPTIBLE_LOW_APP_ADJ);
+ printOomLevel(pw, "BACKUP_APP_ADJ", BACKUP_APP_ADJ);
+ printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", HEAVY_WEIGHT_APP_ADJ);
+ printOomLevel(pw, "SERVICE_ADJ", SERVICE_ADJ);
+ printOomLevel(pw, "HOME_APP_ADJ", HOME_APP_ADJ);
+ printOomLevel(pw, "PREVIOUS_APP_ADJ", PREVIOUS_APP_ADJ);
+ printOomLevel(pw, "SERVICE_B_ADJ", SERVICE_B_ADJ);
+ printOomLevel(pw, "CACHED_APP_MIN_ADJ", CACHED_APP_MIN_ADJ);
+ printOomLevel(pw, "CACHED_APP_MAX_ADJ", CACHED_APP_MAX_ADJ);
+
+ if (needSep) pw.println();
+ pw.print(" Process OOM control ("); pw.print(getLruSizeLocked());
+ pw.print(" total, non-act at ");
+ pw.print(getLruSizeLocked() - mLruProcessActivityStart);
+ pw.print(", non-svc at ");
+ pw.print(getLruSizeLocked() - mLruProcessServiceStart);
+ pw.println("):");
+ dumpProcessOomList(pw, mService, mLruProcesses, " ", "Proc", "PERS", true,
+ dumpPackage);
+ needSep = true;
+ }
+
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ mService.mAppProfiler.dumpProcessesToGc(pw, needSep, dumpPackage);
+ }
+
+ pw.println();
+ mService.mAtmInternal.dumpForOom(pw);
+
+ return true;
+ }
+
+ void registerProcessObserver(IProcessObserver observer) {
+ mProcessObservers.register(observer);
+ }
+
+ void unregisterProcessObserver(IProcessObserver observer) {
+ mProcessObservers.unregister(observer);
+ }
+
+ void dispatchProcessesChanged() {
+ int numOfChanges;
+ synchronized (mProcessChangeLock) {
+ numOfChanges = mPendingProcessChanges.size();
+ if (mActiveProcessChanges.length < numOfChanges) {
+ mActiveProcessChanges = new ProcessChangeItem[numOfChanges];
+ }
+ mPendingProcessChanges.toArray(mActiveProcessChanges);
+ mPendingProcessChanges.clear();
+ if (DEBUG_PROCESS_OBSERVERS) {
+ Slog.i(TAG_PROCESS_OBSERVERS,
+ "*** Delivering " + numOfChanges + " process changes");
+ }
+ }
+
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ for (int j = 0; j < numOfChanges; j++) {
+ ProcessChangeItem item = mActiveProcessChanges[j];
+ if ((item.changes & ProcessChangeItem.CHANGE_ACTIVITIES) != 0) {
+ if (DEBUG_PROCESS_OBSERVERS) {
+ Slog.i(TAG_PROCESS_OBSERVERS,
+ "ACTIVITIES CHANGED pid=" + item.pid + " uid="
+ + item.uid + ": " + item.foregroundActivities);
+ }
+ observer.onForegroundActivitiesChanged(item.pid, item.uid,
+ item.foregroundActivities);
+ }
+ if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES) != 0) {
+ if (DEBUG_PROCESS_OBSERVERS) {
+ Slog.i(TAG_PROCESS_OBSERVERS,
+ "FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid="
+ + item.uid + ": " + item.foregroundServiceTypes);
+ }
+ observer.onForegroundServicesChanged(item.pid, item.uid,
+ item.foregroundServiceTypes);
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mProcessObservers.finishBroadcast();
+
+ synchronized (mProcessChangeLock) {
+ for (int j = 0; j < numOfChanges; j++) {
+ mAvailProcessChanges.add(mActiveProcessChanges[j]);
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ ProcessChangeItem enqueueProcessChangeItemLocked(int pid, int uid) {
+ synchronized (mProcessChangeLock) {
+ int i = mPendingProcessChanges.size() - 1;
+ ActivityManagerService.ProcessChangeItem item = null;
+ while (i >= 0) {
+ item = mPendingProcessChanges.get(i);
+ if (item.pid == pid) {
+ if (DEBUG_PROCESS_OBSERVERS) {
+ Slog.i(TAG_PROCESS_OBSERVERS, "Re-using existing item: " + item);
+ }
+ break;
+ }
+ i--;
+ }
+
+ if (i < 0) {
+ // No existing item in pending changes; need a new one.
+ final int num = mAvailProcessChanges.size();
+ if (num > 0) {
+ item = mAvailProcessChanges.remove(num - 1);
+ if (DEBUG_PROCESS_OBSERVERS) {
+ Slog.i(TAG_PROCESS_OBSERVERS, "Retrieving available item: " + item);
+ }
+ } else {
+ item = new ActivityManagerService.ProcessChangeItem();
+ if (DEBUG_PROCESS_OBSERVERS) {
+ Slog.i(TAG_PROCESS_OBSERVERS, "Allocating new item: " + item);
+ }
+ }
+ item.changes = 0;
+ item.pid = pid;
+ item.uid = uid;
+ if (mPendingProcessChanges.size() == 0) {
+ if (DEBUG_PROCESS_OBSERVERS) {
+ Slog.i(TAG_PROCESS_OBSERVERS, "*** Enqueueing dispatch processes changed!");
+ }
+ mService.mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG)
+ .sendToTarget();
+ }
+ mPendingProcessChanges.add(item);
+ }
+
+ return item;
+ }
+ }
+
+ @GuardedBy("mService")
+ void scheduleDispatchProcessDiedLocked(int pid, int uid) {
+ synchronized (mProcessChangeLock) {
+ for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
+ ProcessChangeItem item = mPendingProcessChanges.get(i);
+ if (pid > 0 && item.pid == pid) {
+ mPendingProcessChanges.remove(i);
+ mAvailProcessChanges.add(item);
+ }
+ }
+ mService.mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, pid, uid,
+ null).sendToTarget();
+ }
+ }
+
+ void dispatchProcessDied(int pid, int uid) {
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ observer.onProcessDied(pid, uid);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mProcessObservers.finishBroadcast();
+ }
+
@GuardedBy("mService")
ArrayList<ProcessRecord> collectProcessesLocked(int start, boolean allPkgs, String[] args) {
ArrayList<ProcessRecord> procs;
@@ -3668,8 +4411,8 @@
ProcessRecord proc = mLruProcesses.get(i);
if (proc.pid > 0 && proc.pid == pid) {
procs.add(proc);
- } else if (allPkgs && proc.pkgList != null
- && proc.pkgList.containsKey(args[start])) {
+ } else if (allPkgs && proc.getPkgList() != null
+ && proc.getPkgList().containsKey(args[start])) {
procs.add(proc);
} else if (proc.processName.equals(args[start])) {
procs.add(proc);
@@ -3697,27 +4440,23 @@
continue;
}
- final int packageCount = app.pkgList.size();
- for (int j = 0; j < packageCount; j++) {
- final String packageName = app.pkgList.keyAt(j);
- if (!updateFrameworkRes && !packagesToUpdate.contains(packageName)) {
- continue;
- }
- try {
- final ApplicationInfo ai = AppGlobals.getPackageManager()
- .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
- if (ai == null) {
- continue;
+ app.getPkgList().forEachPackage(packageName -> {
+ if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
+ try {
+ final ApplicationInfo ai = AppGlobals.getPackageManager()
+ .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
+ if (ai != null) {
+ app.thread.scheduleApplicationInfoChanged(ai);
+ if (ai.packageName.equals(app.info.packageName)) {
+ app.info = ai;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
+ packageName, app));
}
- app.thread.scheduleApplicationInfoChanged(ai);
- if (ai.packageName.equals(app.info.packageName)) {
- app.info = ai;
- }
- } catch (RemoteException e) {
- Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
- packageName, app));
}
- }
+ });
}
}
@@ -4103,7 +4842,7 @@
boolean diff = mIdle != idle;
mIdle = idle;
if (diff && idle) {
- synchronized (this) {
+ synchronized (mService) {
if (mWorkItems.size() > 0) {
mHandler.sendEmptyMessage(H.MSG_DEVICE_IDLE);
}
@@ -4176,13 +4915,14 @@
return true;
}
- final int pkgSize = app.pkgList.size();
- for (int ip = 0; ip < pkgSize; ip++) {
- if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.contains(
- app.pkgList.keyAt(ip))) {
+ if (app.getPkgList().forEachPackage(pkgName -> {
+ if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.contains(pkgName)) {
// One of the packages in this process is exempted
- return true;
+ return Boolean.TRUE;
}
+ return null;
+ }) != null) {
+ return true;
}
if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.contains(
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
new file mode 100644
index 0000000..9fd2bd7
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -0,0 +1,653 @@
+/*
+ * 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.am;
+
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.app.ActivityManager.processStateAmToProto;
+
+import android.app.IApplicationThread;
+import android.content.pm.ApplicationInfo;
+import android.os.Debug;
+import android.os.SystemClock;
+import android.util.DebugUtils;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.procstats.ProcessState;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.am.ProcessList.ProcStateMemTracker;
+
+import java.io.PrintWriter;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Profiling info of the process, such as PSS, cpu, etc.
+ */
+final class ProcessProfileRecord {
+ final ProcessRecord mApp;
+
+ private final ActivityManagerService mService;
+
+ final Object mProfilerLock;
+
+ @GuardedBy("mProfilerLock")
+ private final ProcessList.ProcStateMemTracker mProcStateMemTracker =
+ new ProcessList.ProcStateMemTracker();
+
+ /**
+ * Stats of pss, cpu, etc.
+ */
+ @GuardedBy("mService.mProcessStats.mLock")
+ private ProcessState mBaseProcessTracker;
+
+ /**
+ * Last time we retrieved PSS data.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mLastPssTime;
+
+ /**
+ * Next time we want to request PSS data.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mNextPssTime;
+
+ /**
+ * Initial memory pss of process for idle maintenance.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mInitialIdlePss;
+
+ /**
+ * Last computed memory pss.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mLastPss;
+
+ /**
+ * Last computed SwapPss.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mLastSwapPss;
+
+ /**
+ * Last computed pss when in cached state.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mLastCachedPss;
+
+ /**
+ * Last computed SwapPss when in cached state.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mLastCachedSwapPss;
+
+ /**
+ * Last computed memory rss.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mLastRss;
+
+ /**
+ * Cache of last retrieve memory info, to throttle how frequently apps can request it.
+ */
+ @GuardedBy("mProfilerLock")
+ private Debug.MemoryInfo mLastMemInfo;
+
+ /**
+ * Cache of last retrieve memory uptime, to throttle how frequently apps can request it.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mLastMemInfoTime;
+
+ /**
+ * Currently requesting pss for.
+ */
+ @GuardedBy("mProfilerLock")
+ private int mPssProcState = PROCESS_STATE_NONEXISTENT;
+
+ /**
+ * The type of stat collection that we are currently requesting.
+ */
+ @GuardedBy("mProfilerLock")
+ private int mPssStatType;
+
+ /**
+ * How long proc has run CPU at last check.
+ */
+ final AtomicLong mLastCpuTime = new AtomicLong(0);
+
+ /**
+ * How long proc has run CPU most recently.
+ */
+ final AtomicLong mCurCpuTime = new AtomicLong(0);
+
+ /**
+ * Last selected memory trimming level.
+ */
+ @GuardedBy("mService")
+ private int mTrimMemoryLevel;
+
+ /**
+ * Want to clean up resources from showing UI?
+ */
+ @GuardedBy("mService")
+ private boolean mPendingUiClean;
+
+ /**
+ * Pointer to the battery stats of this process.
+ */
+ private BatteryStatsImpl.Uid.Proc mCurProcBatteryStats;
+
+ /**
+ * When we last asked the app to do a gc.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mLastRequestedGc;
+
+ /**
+ * When we last told the app that memory is low.
+ */
+ @GuardedBy("mService")
+ private long mLastLowMemory;
+
+ /**
+ * Set to true when waiting to report low mem.
+ */
+ @GuardedBy("mProfilerLock")
+ private boolean mReportLowMemory;
+
+ // ========================================================================
+ // Local copies of some process info, to avoid holding global AMS lock
+ @GuardedBy("mProfilerLock")
+ private int mPid;
+
+ @GuardedBy("mProfilerLock")
+ private IApplicationThread mThread;
+
+ @GuardedBy("mProfilerLock")
+ private int mSetProcState;
+
+ @GuardedBy("mProfilerLock")
+ private int mSetAdj;
+
+ @GuardedBy("mProfilerLock")
+ private int mCurRawAdj;
+
+ @GuardedBy("mProfilerLock")
+ private long mLastStateTime;
+
+ ProcessProfileRecord(final ProcessRecord app) {
+ mApp = app;
+ mService = app.mService;
+ mProfilerLock = mService.mAppProfiler.mProfilerLock;
+ }
+
+ void init(long now) {
+ mLastPssTime = mNextPssTime = now;
+ }
+
+ @GuardedBy("mService.mProcessStats.mLock")
+ ProcessState getBaseProcessTracker() {
+ return mBaseProcessTracker;
+ }
+
+ @GuardedBy("mService.mProcessStats.mLock")
+ void setBaseProcessTracker(ProcessState baseProcessTracker) {
+ mBaseProcessTracker = baseProcessTracker;
+ }
+
+ void onProcessActive(IApplicationThread thread, ProcessStatsService tracker) {
+ if (mThread == null) {
+ synchronized (mProfilerLock) {
+ synchronized (tracker.mLock) {
+ final ProcessState origBase = getBaseProcessTracker();
+ final PackageList pkgList = mApp.getPkgList();
+ if (origBase != null) {
+ synchronized (pkgList) {
+ origBase.setState(ProcessStats.STATE_NOTHING,
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
+ pkgList.getPackageListLocked());
+ pkgList.forEachPackage((pkgName, holder) ->
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ mApp.uid, mApp.processName, pkgName,
+ processStateAmToProto(ProcessStats.STATE_NOTHING),
+ holder.appVersion)
+ );
+ }
+ origBase.makeInactive();
+ }
+ final ApplicationInfo info = mApp.info;
+ final ProcessState baseProcessTracker = tracker.getProcessStateLocked(
+ info.packageName, info.uid, info.longVersionCode, mApp.processName);
+ setBaseProcessTracker(baseProcessTracker);
+ baseProcessTracker.makeActive();
+ pkgList.forEachPackage((pkgName, holder) -> {
+ if (holder.state != null && holder.state != origBase) {
+ holder.state.makeInactive();
+ }
+ tracker.updateProcessStateHolderLocked(holder, pkgName, mApp.info.uid,
+ mApp.info.longVersionCode, mApp.processName);
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
+ }
+ });
+ mThread = thread;
+ }
+ }
+ } else {
+ synchronized (mProfilerLock) {
+ mThread = thread;
+ }
+ }
+ }
+
+ void onProcessInactive(ProcessStatsService tracker) {
+ synchronized (mProfilerLock) {
+ synchronized (tracker.mLock) {
+ final ProcessState origBase = getBaseProcessTracker();
+ if (origBase != null) {
+ final PackageList pkgList = mApp.getPkgList();
+ synchronized (pkgList) {
+ origBase.setState(ProcessStats.STATE_NOTHING,
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
+ pkgList.getPackageListLocked());
+ pkgList.forEachPackage((pkgName, holder) ->
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ mApp.uid, mApp.processName, pkgName,
+ processStateAmToProto(ProcessStats.STATE_NOTHING),
+ holder.appVersion)
+ );
+ }
+ origBase.makeInactive();
+ setBaseProcessTracker(null);
+ pkgList.forEachPackageProcessStats(holder -> {
+ if (holder.state != null && holder.state != origBase) {
+ holder.state.makeInactive();
+ }
+ holder.pkg = null;
+ holder.state = null;
+ });
+ }
+ mThread = null;
+ }
+ }
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getLastPssTime() {
+ return mLastPssTime;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastPssTime(long lastPssTime) {
+ mLastPssTime = lastPssTime;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getNextPssTime() {
+ return mNextPssTime;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setNextPssTime(long nextPssTime) {
+ mNextPssTime = nextPssTime;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getInitialIdlePss() {
+ return mInitialIdlePss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setInitialIdlePss(long initialIdlePss) {
+ mInitialIdlePss = initialIdlePss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getLastPss() {
+ return mLastPss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastPss(long lastPss) {
+ mLastPss = lastPss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getLastCachedPss() {
+ return mLastCachedPss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastCachedPss(long lastCachedPss) {
+ mLastCachedPss = lastCachedPss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getLastSwapPss() {
+ return mLastSwapPss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastSwapPss(long lastSwapPss) {
+ mLastSwapPss = lastSwapPss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getLastCachedSwapPss() {
+ return mLastCachedSwapPss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastCachedSwapPss(long lastCachedSwapPss) {
+ mLastCachedSwapPss = lastCachedSwapPss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getLastRss() {
+ return mLastRss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastRss(long lastRss) {
+ mLastRss = lastRss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ Debug.MemoryInfo getLastMemInfo() {
+ return mLastMemInfo;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastMemInfo(Debug.MemoryInfo lastMemInfo) {
+ mLastMemInfo = lastMemInfo;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getLastMemInfoTime() {
+ return mLastMemInfoTime;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastMemInfoTime(long lastMemInfoTime) {
+ mLastMemInfoTime = lastMemInfoTime;
+ }
+
+ @GuardedBy("mProfilerLock")
+ int getPssProcState() {
+ return mPssProcState;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setPssProcState(int pssProcState) {
+ mPssProcState = pssProcState;
+ }
+
+ @GuardedBy("mProfilerLock")
+ int getPssStatType() {
+ return mPssStatType;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setPssStatType(int pssStatType) {
+ mPssStatType = pssStatType;
+ }
+
+ @GuardedBy("mService")
+ int getTrimMemoryLevel() {
+ return mTrimMemoryLevel;
+ }
+
+ @GuardedBy("mService")
+ void setTrimMemoryLevel(int trimMemoryLevel) {
+ mTrimMemoryLevel = trimMemoryLevel;
+ }
+
+ @GuardedBy("mService")
+ boolean hasPendingUiClean() {
+ return mPendingUiClean;
+ }
+
+ @GuardedBy("mService")
+ void setPendingUiClean(boolean pendingUiClean) {
+ mPendingUiClean = pendingUiClean;
+ mApp.getWindowProcessController().setPendingUiClean(pendingUiClean);
+ }
+
+ BatteryStatsImpl.Uid.Proc getCurProcBatteryStats() {
+ return mCurProcBatteryStats;
+ }
+
+ void setCurProcBatteryStats(BatteryStatsImpl.Uid.Proc curProcBatteryStats) {
+ mCurProcBatteryStats = curProcBatteryStats;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getLastRequestedGc() {
+ return mLastRequestedGc;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastRequestedGc(long lastRequestedGc) {
+ mLastRequestedGc = lastRequestedGc;
+ }
+
+ @GuardedBy("mService")
+ long getLastLowMemory() {
+ return mLastLowMemory;
+ }
+
+ @GuardedBy("mService")
+ void setLastLowMemory(long lastLowMemory) {
+ mLastLowMemory = lastLowMemory;
+ }
+
+ @GuardedBy("mProfilerLock")
+ boolean getReportLowMemory() {
+ return mReportLowMemory;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setReportLowMemory(boolean reportLowMemory) {
+ mReportLowMemory = reportLowMemory;
+ }
+
+ void addPss(long pss, long uss, long rss, boolean always, int type, long duration) {
+ synchronized (mService.mProcessStats.mLock) {
+ final ProcessState tracker = mBaseProcessTracker;
+ if (tracker != null) {
+ final PackageList pkgList = mApp.getPkgList();
+ synchronized (pkgList) {
+ tracker.addPss(pss, uss, rss, always, type, duration,
+ pkgList.getPackageListLocked());
+ }
+ }
+ }
+ }
+
+ void reportExcessiveCpu() {
+ synchronized (mService.mProcessStats.mLock) {
+ final ProcessState tracker = mBaseProcessTracker;
+ if (tracker != null) {
+ final PackageList pkgList = mApp.getPkgList();
+ synchronized (pkgList) {
+ tracker.reportExcessiveCpu(pkgList.getPackageListLocked());
+ }
+ }
+ }
+ }
+
+ void reportCachedKill() {
+ synchronized (mService.mProcessStats.mLock) {
+ final ProcessState tracker = mBaseProcessTracker;
+ if (tracker != null) {
+ final PackageList pkgList = mApp.getPkgList();
+ synchronized (pkgList) {
+ tracker.reportCachedKill(pkgList.getPackageListLocked(), mLastCachedPss);
+ pkgList.forEachPackageProcessStats(holder ->
+ FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED,
+ mApp.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ mLastCachedPss,
+ holder.appVersion)
+ );
+ }
+ }
+ }
+ }
+
+ void setProcessTrackerState(int procState, int memFactor, long now) {
+ synchronized (mService.mProcessStats.mLock) {
+ final ProcessState tracker = mBaseProcessTracker;
+ if (tracker != null) {
+ if (procState != PROCESS_STATE_NONEXISTENT) {
+ final PackageList pkgList = mApp.getPkgList();
+ synchronized (pkgList) {
+ tracker.setState(procState, memFactor, now,
+ pkgList.getPackageListLocked());
+ }
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mProfilerLock")
+ void commitNextPssTime() {
+ commitNextPssTime(mProcStateMemTracker);
+ }
+
+ @GuardedBy("mProfilerLock")
+ void abortNextPssTime() {
+ abortNextPssTime(mProcStateMemTracker);
+ }
+
+ @GuardedBy("mProfilerLock")
+ long computeNextPssTime(int procState, boolean test, boolean sleeping, long now) {
+ return ProcessList.computeNextPssTime(procState, mProcStateMemTracker, test, sleeping, now);
+ }
+
+ private static void commitNextPssTime(ProcStateMemTracker tracker) {
+ if (tracker.mPendingMemState >= 0) {
+ tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
+ tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
+ tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
+ tracker.mPendingMemState = -1;
+ }
+ }
+
+ private static void abortNextPssTime(ProcStateMemTracker tracker) {
+ tracker.mPendingMemState = -1;
+ }
+
+ @GuardedBy("mProfilerLock")
+ int getPid() {
+ return mPid;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setPid(int pid) {
+ mPid = pid;
+ }
+
+ @GuardedBy("mProfilerLock")
+ IApplicationThread getThread() {
+ return mThread;
+ }
+
+ @GuardedBy("mProfilerLock")
+ int getSetProcState() {
+ return mSetProcState;
+ }
+
+ @GuardedBy("mProfilerLock")
+ int getSetAdj() {
+ return mSetAdj;
+ }
+
+ @GuardedBy("mProfilerLock")
+ int getCurRawAdj() {
+ return mCurRawAdj;
+ }
+
+ @GuardedBy("mProfilerLock")
+ long getLastStateTime() {
+ return mLastStateTime;
+ }
+
+ @GuardedBy({"mService", "mProfilerLock"})
+ void updateProcState(ProcessRecord app) {
+ mSetProcState = app.getCurProcState();
+ mSetAdj = app.curAdj;
+ mCurRawAdj = app.getCurRawAdj();
+ mLastStateTime = app.lastStateTime;
+ }
+
+ @GuardedBy("mService")
+ void dumpPss(PrintWriter pw, String prefix, long nowUptime) {
+ synchronized (mProfilerLock) {
+ pw.print(" lastPssTime=");
+ TimeUtils.formatDuration(mLastPssTime, nowUptime, pw);
+ pw.print(" pssProcState=");
+ pw.print(mPssProcState);
+ pw.print(" pssStatType=");
+ pw.print(mPssStatType);
+ pw.print(" nextPssTime=");
+ TimeUtils.formatDuration(mNextPssTime, nowUptime, pw);
+ pw.println();
+ pw.print(prefix);
+ pw.print("lastPss=");
+ DebugUtils.printSizeValue(pw, mLastPss * 1024);
+ pw.print(" lastSwapPss=");
+ DebugUtils.printSizeValue(pw, mLastSwapPss * 1024);
+ pw.print(" lastCachedPss=");
+ DebugUtils.printSizeValue(pw, mLastCachedPss * 1024);
+ pw.print(" lastCachedSwapPss=");
+ DebugUtils.printSizeValue(pw, mLastCachedSwapPss * 1024);
+ pw.print(" lastRss=");
+ DebugUtils.printSizeValue(pw, mLastRss * 1024);
+ pw.println();
+ pw.print(prefix);
+ pw.print(" trimMemoryLevel=");
+ pw.println(mTrimMemoryLevel);
+ pw.println();
+ pw.print(prefix); pw.print("procStateMemTracker: ");
+ mProcStateMemTracker.dumpLine(pw);
+ pw.print(prefix);
+ pw.print("lastRequestedGc=");
+ TimeUtils.formatDuration(mLastRequestedGc, nowUptime, pw);
+ pw.print(" lastLowMemory=");
+ TimeUtils.formatDuration(mLastLowMemory, nowUptime, pw);
+ pw.print(" reportLowMemory=");
+ pw.println(mReportLowMemory);
+ }
+ }
+
+ void dumpCputime(PrintWriter pw, String prefix) {
+ final long lastCpuTime = mLastCpuTime.get();
+ pw.print(prefix);
+ pw.print("lastCpuTime=");
+ pw.print(lastCpuTime);
+ if (lastCpuTime > 0) {
+ pw.print(" timeUsed=");
+ TimeUtils.formatDuration(mCurCpuTime.get() - lastCpuTime, pw);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 63195d3..52ff427 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -28,6 +28,17 @@
import static android.os.Process.SYSTEM_UID;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED;
+import static com.android.server.am.ActiveServices.fgsCodeToString;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -52,7 +63,6 @@
import android.content.pm.VersionedPackage;
import android.content.res.CompatibilityInfo;
import android.os.Binder;
-import android.os.Debug;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
@@ -74,7 +84,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.Zygote;
import com.android.internal.util.FrameworkStatsLog;
@@ -96,9 +105,9 @@
* is currently running.
*/
class ProcessRecord implements WindowProcessListener {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;
+ static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;
- private final ActivityManagerService mService; // where we came from
+ final ActivityManagerService mService; // where we came from
volatile ApplicationInfo info; // all about the first app in the process
final ProcessInfo processInfo; // if non-null, process-specific manifest info
final boolean isolated; // true if this is a special isolated process
@@ -106,51 +115,17 @@
final int uid; // uid of process; may be different from 'info' if isolated
final int userId; // user of process.
final String processName; // name of the process
- // List of packages running in the process
- final PackageList pkgList = new PackageList();
- final class PackageList {
- final ArrayMap<String, ProcessStats.ProcessStateHolder> mPkgList = new ArrayMap<>();
- ProcessStats.ProcessStateHolder put(String key, ProcessStats.ProcessStateHolder value) {
- mWindowProcessController.addPackage(key);
- return mPkgList.put(key, value);
- }
+ /**
+ * List of packages running in the process
+ */
+ private final PackageList mPkgList = new PackageList(this);
- void clear() {
- mPkgList.clear();
- mWindowProcessController.clearPackageList();
- }
-
- int size() {
- return mPkgList.size();
- }
-
- String keyAt(int index) {
- return mPkgList.keyAt(index);
- }
-
- public ProcessStats.ProcessStateHolder valueAt(int index) {
- return mPkgList.valueAt(index);
- }
-
- ProcessStats.ProcessStateHolder get(String pkgName) {
- return mPkgList.get(pkgName);
- }
-
- boolean containsKey(Object key) {
- return mPkgList.containsKey(key);
- }
- }
-
- final ProcessList.ProcStateMemTracker procStateMemTracker
- = new ProcessList.ProcStateMemTracker();
UidRecord uidRecord; // overall state of process's uid.
ArraySet<String> pkgDeps; // additional packages we have a dependency on
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
// are in the process of launching the app)
- ProcessState baseProcessTracker;
- BatteryStatsImpl.Uid.Proc curProcBatteryStats;
int pid; // The process of this application; 0 if none
String procStatFile; // path to /proc/<pid>/stat
int[] gids; // The gids this process was launched with
@@ -158,14 +133,7 @@
String instructionSet; // The instruction set this process was launched with
boolean starting; // True if the process is being started
long lastActivityTime; // For managing the LRU list
- long lastPssTime; // Last time we retrieved PSS data
- long nextPssTime; // Next time we want to request PSS data
long lastStateTime; // Last time setProcState changed
- long initialIdlePss; // Initial memory pss of process for idle maintenance.
- long lastPss; // Last computed memory pss.
- long lastSwapPss; // Last computed SwapPss.
- long lastCachedPss; // Last computed pss when in cached state.
- long lastCachedSwapPss; // Last computed SwapPss when in cached state.
int maxAdj; // Maximum OOM adjustment for this process
private int mCurRawAdj; // Current OOM unlimited adjustment for this process
int setRawAdj; // Last set OOM unlimited adjustment for this process
@@ -184,13 +152,10 @@
boolean shouldNotFreeze; // True if a process has a WPRI binding from an unfrozen process
private int mCurSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
- int trimMemoryLevel; // Last selected memory trimming level
private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
private int mCurRawProcState = PROCESS_STATE_NONEXISTENT; // Temp state during computation
int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
- int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
- int pssStatType; // The type of stat collection that we are currently requesting
int savedPriority; // Previous priority value if we're switching to non-SCHED_OTHER
int renderThreadTid; // TID for RenderThread
ServiceRecord connectionService; // Service that applied current connectionGroup/Importance
@@ -226,7 +191,6 @@
// performance, as well as oom adj score will be set to
// ProcessList#VISIBLE_APP_ADJ at minimum to reduce the chance
// of the process getting killed.
- private boolean mPendingUiClean; // Want to clean up resources from showing UI?
boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so want to be lower
boolean treatLikeActivity; // Bound using BIND_TREAT_LIKE_ACTIVITY
boolean bad; // True if disabled in the bad process list
@@ -250,13 +214,8 @@
private boolean mUsingWrapper; // Set to true when process was launched with a wrapper attached
final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();// receivers currently running in the app
private long mWhenUnimportant; // When (uptime) the process last became unimportant
- long lastCpuTime; // How long proc has run CPU at last check
- long curCpuTime; // How long proc has run CPU most recently
- long lastRequestedGc; // When we last asked the app to do a gc
- long lastLowMemory; // When we last told the app that memory is low
long lastProviderTime; // The last time someone else was using a provider in this process.
long lastTopTime; // The last time the process was in the TOP state or greater.
- boolean reportLowMemory; // Set to true when waiting to report low mem
boolean empty; // Is this an empty background process?
private boolean mCached; // Is this a cached process?
String adjType; // Debugging: primary thing impacting oom_adj.
@@ -267,11 +226,6 @@
Runnable crashHandler; // Optional local handler to be invoked in the process crash.
boolean bindMountPending; // True if Android/obb and Android/data need to be bind mount .
- // Cache of last retrieve memory info and uptime, to throttle how frequently
- // apps can requyest it.
- Debug.MemoryInfo lastMemInfo;
- long lastMemInfoTime;
-
// Controller for error dialogs
private final ErrorDialogController mDialogController = new ErrorDialogController();
// Controller for driving the process state on the window manager side.
@@ -323,8 +277,8 @@
// Process is currently hosting a backup agent for backup or restore
public boolean inFullBackup;
- // App is allowed to manage whitelists such as temporary Power Save mode whitelist.
- boolean whitelistManager;
+ // App is allowed to manage allowlists such as temporary Power Save mode allowlist.
+ boolean mAllowlistManager;
// Params used in starting this process.
HostingRecord hostingRecord;
@@ -335,8 +289,6 @@
// set of disabled compat changes for the process (all others are enabled)
long[] mDisabledCompatChanges;
- long mLastRss; // Last computed memory rss.
-
// The precede instance of the process, which would exist when the previous process is killed
// but not fully dead yet; in this case, the new instance of the process should be held until
// this precede instance is fully dead.
@@ -378,16 +330,17 @@
private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>();
- // The list of permissions that can start FGS from background.
- private static String[] ALLOW_BG_START_FGS_PERMISSIONS =
- {START_ACTIVITIES_FROM_BACKGROUND, START_FOREGROUND_SERVICES_FROM_BACKGROUND,
- SYSTEM_ALERT_WINDOW};
// Does the process has permission to start FGS from background.
- boolean mAllowStartFgsByPermission;
+ @ActiveServices.FgsFeatureRetCode int mAllowStartFgsByPermission;
// Can this process start FGS from background?
// If this process has the ability to start FGS from background, this ability can be passed to
// another process through service binding.
- boolean mAllowStartFgs;
+ @ActiveServices.FgsFeatureRetCode int mAllowStartFgs;
+
+ /**
+ * Profiling info of the process, such as PSS, cpu, etc.
+ */
+ final ProcessProfileRecord mProfile;
void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
long startTime) {
@@ -439,11 +392,7 @@
pw.print(prefix); pw.print("dir="); pw.print(info.sourceDir);
pw.print(" publicDir="); pw.print(info.publicSourceDir);
pw.print(" data="); pw.println(info.dataDir);
- pw.print(prefix); pw.print("packageList={");
- for (int i=0; i<pkgList.size(); i++) {
- if (i > 0) pw.print(", ");
- pw.print(pkgList.keyAt(i));
- }
+ mPkgList.dump(pw, prefix);
pw.println("}");
if (pkgDeps != null) {
pw.print(prefix); pw.print("packageDependencies={");
@@ -462,21 +411,7 @@
pw.println(starting);
pw.print(prefix); pw.print("lastActivityTime=");
TimeUtils.formatDuration(lastActivityTime, nowUptime, pw);
- pw.print(" lastPssTime=");
- TimeUtils.formatDuration(lastPssTime, nowUptime, pw);
- pw.print(" pssStatType="); pw.print(pssStatType);
- pw.print(" nextPssTime=");
- TimeUtils.formatDuration(nextPssTime, nowUptime, pw);
pw.println();
- pw.print(prefix); pw.print("lastPss="); DebugUtils.printSizeValue(pw, lastPss * 1024);
- pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, lastSwapPss * 1024);
- pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, lastCachedPss * 1024);
- pw.print(" lastCachedSwapPss="); DebugUtils.printSizeValue(pw,
- lastCachedSwapPss * 1024);
- pw.print(" lastRss="); DebugUtils.printSizeValue(pw, mLastRss * 1024);
- pw.println();
- pw.print(prefix); pw.print("procStateMemTracker: ");
- procStateMemTracker.dumpLine(pw);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.println(lruSeq);
pw.print(prefix); pw.print("oom adj: max="); pw.print(maxAdj);
@@ -489,10 +424,8 @@
pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
pw.print(" setSchedGroup="); pw.print(setSchedGroup);
pw.print(" systemNoUi="); pw.print(systemNoUi);
- pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
pw.print(prefix); pw.print("curProcState="); pw.print(getCurProcState());
pw.print(" mRepProcState="); pw.print(mRepProcState);
- pw.print(" pssProcState="); pw.print(pssProcState);
pw.print(" setProcState="); pw.print(setProcState);
pw.print(" lastStateTime=");
TimeUtils.formatDuration(lastStateTime, nowUptime, pw);
@@ -504,14 +437,15 @@
pw.println();
pw.print(prefix); pw.print("allowStartFgsState=");
pw.println(mAllowStartFgsState);
- if (mAllowStartFgs) {
- pw.print(prefix); pw.print("allowStartFgs="); pw.println(mAllowStartFgs);
+ if (mAllowStartFgs != FGS_FEATURE_DENIED) {
+ pw.print(prefix); pw.print("allowStartFgs=");
+ pw.println(fgsCodeToString(mAllowStartFgs));
}
- if (hasShownUi || mPendingUiClean || hasAboveClient || treatLikeActivity) {
+ if (hasShownUi || mProfile.hasPendingUiClean() || hasAboveClient || treatLikeActivity) {
pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
- pw.print(" pendingUiClean="); pw.print(mPendingUiClean);
- pw.print(" hasAboveClient="); pw.print(hasAboveClient);
- pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
+ pw.print(" pendingUiClean="); pw.print(mProfile.hasPendingUiClean());
+ pw.print(" hasAboveClient="); pw.print(hasAboveClient);
+ pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
pw.print(prefix); pw.print("cached="); pw.print(mCached);
pw.print(" empty="); pw.println(empty);
@@ -521,7 +455,7 @@
}
if (notCachedSinceIdle) {
pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(notCachedSinceIdle);
- pw.print(" initialIdlePss="); pw.println(initialIdlePss);
+ pw.print(" initialIdlePss="); pw.println(mProfile.getInitialIdlePss());
}
if (connectionService != null || connectionGroup != 0) {
pw.print(prefix); pw.print("connectionGroup="); pw.print(connectionGroup);
@@ -579,20 +513,12 @@
pw.print(prefix); pw.print("mountMode="); pw.println(
DebugUtils.valueToString(Zygote.class, "MOUNT_EXTERNAL_", mountMode));
if (setProcState > ActivityManager.PROCESS_STATE_SERVICE) {
- pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime);
- if (lastCpuTime > 0) {
- pw.print(" timeUsed=");
- TimeUtils.formatDuration(curCpuTime - lastCpuTime, pw);
- }
- pw.print(" whenUnimportant=");
- TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw);
- pw.println();
+ mProfile.dumpCputime(pw, prefix);
+ pw.print(" whenUnimportant=");
+ TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw);
+ pw.println();
}
- pw.print(prefix); pw.print("lastRequestedGc=");
- TimeUtils.formatDuration(lastRequestedGc, nowUptime, pw);
- pw.print(" lastLowMemory=");
- TimeUtils.formatDuration(lastLowMemory, nowUptime, pw);
- pw.print(" reportLowMemory="); pw.println(reportLowMemory);
+ mProfile.dumpPss(pw, prefix, nowUptime);
if (killed || killedByAm || waitingToKill != null) {
pw.print(prefix); pw.print("killed="); pw.print(killed);
pw.print(" killedByAm="); pw.print(killedByAm);
@@ -614,8 +540,8 @@
}
pw.println();
}
- if (whitelistManager) {
- pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
+ if (mAllowlistManager) {
+ pw.print(prefix); pw.print("allowlistManager="); pw.println(mAllowlistManager);
}
if (isolatedEntryPoint != null || isolatedEntryPointArgs != null) {
pw.print(prefix); pw.print("isolatedEntryPoint="); pw.println(isolatedEntryPoint);
@@ -699,53 +625,33 @@
curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
mPersistent = false;
removed = false;
- freezeUnfreezeTime = lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
+ mProfile = new ProcessProfileRecord(this);
+ final long now = SystemClock.uptimeMillis();
+ freezeUnfreezeTime = lastStateTime = now;
+ mProfile.init(now);
mWindowProcessController = new WindowProcessController(
mService.mActivityTaskManager, info, processName, uid, userId, this, this);
- pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
+ mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
setAllowStartFgsByPermission();
}
+ PackageList getPkgList() {
+ return mPkgList;
+ }
+
public void setPid(int _pid) {
pid = _pid;
mWindowProcessController.setPid(pid);
procStatFile = null;
shortStringName = null;
stringName = null;
+ synchronized (mProfile.mProfilerLock) {
+ mProfile.setPid(pid);
+ }
}
public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
- if (thread == null) {
- synchronized (tracker.mLock) {
- final ProcessState origBase = baseProcessTracker;
- if (origBase != null) {
- origBase.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
- pkgList.mPkgList);
- for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgList.keyAt(ipkg),
- ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
- pkgList.valueAt(ipkg).appVersion);
- }
- origBase.makeInactive();
- }
- baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
- info.longVersionCode, processName);
- baseProcessTracker.makeActive();
- for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) {
- ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
- if (holder.state != null && holder.state != origBase) {
- holder.state.makeInactive();
- }
- tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid,
- info.longVersionCode, processName);
- if (holder.state != baseProcessTracker) {
- holder.state.makeActive();
- }
- }
- }
- }
+ mProfile.onProcessActive(_thread, tracker);
thread = _thread;
mWindowProcessController.setThread(thread);
}
@@ -753,32 +659,7 @@
public void makeInactive(ProcessStatsService tracker) {
thread = null;
mWindowProcessController.setThread(null);
- synchronized (tracker.mLock) {
- final ProcessState origBase = baseProcessTracker;
- if (origBase != null) {
- if (origBase != null) {
- origBase.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
- pkgList.mPkgList);
- for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgList.keyAt(ipkg),
- ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
- pkgList.valueAt(ipkg).appVersion);
- }
- origBase.makeInactive();
- }
- baseProcessTracker = null;
- for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) {
- ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
- if (holder.state != null && holder.state != origBase) {
- holder.state.makeInactive();
- }
- holder.pkg = null;
- holder.state = null;
- }
- }
- }
+ mProfile.onProcessInactive(tracker);
}
/**
@@ -1080,22 +961,25 @@
* Return true if package has been added false if not
*/
public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) {
- if (!pkgList.containsKey(pkg)) {
- synchronized (tracker.mLock) {
- ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
- versionCode);
- if (baseProcessTracker != null) {
- tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
- processName);
- pkgList.put(pkg, holder);
- if (holder.state != baseProcessTracker) {
- holder.state.makeActive();
+ synchronized (tracker.mLock) {
+ synchronized (mPkgList) {
+ if (!mPkgList.containsKey(pkg)) {
+ ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+ versionCode);
+ final ProcessState baseProcessTracker = mProfile.getBaseProcessTracker();
+ if (baseProcessTracker != null) {
+ tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
+ processName);
+ mPkgList.put(pkg, holder);
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
+ }
+ } else {
+ mPkgList.put(pkg, holder);
}
- } else {
- pkgList.put(pkg, holder);
+ return true;
}
}
- return true;
}
return false;
}
@@ -1114,12 +998,12 @@
mRepProcState = newState;
setCurProcState(newState);
setCurRawProcState(newState);
- for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgList.keyAt(ipkg),
+ getPkgList().forEachPackage((pkgName, holder) ->
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgName,
ActivityManager.processStateAmToProto(mRepProcState),
- pkgList.valueAt(ipkg).appVersion);
- }
+ holder.appVersion)
+ );
}
}
@@ -1127,66 +1011,51 @@
* Delete all packages from list except the package indicated in info
*/
public void resetPackageList(ProcessStatsService tracker) {
- final int N = pkgList.size();
synchronized (tracker.mLock) {
- if (baseProcessTracker != null) {
- long now = SystemClock.uptimeMillis();
- baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), now, pkgList.mPkgList);
- for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgList.keyAt(ipkg),
- ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
- pkgList.valueAt(ipkg).appVersion);
- }
- if (N != 1) {
- for (int i = 0; i < N; i++) {
- ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
- if (holder.state != null && holder.state != baseProcessTracker) {
- holder.state.makeInactive();
+ final ProcessState baseProcessTracker = mProfile.getBaseProcessTracker();
+ synchronized (mPkgList) {
+ final int numOfPkgs = mPkgList.size();
+ if (baseProcessTracker != null) {
+ long now = SystemClock.uptimeMillis();
+ baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
+ tracker.getMemFactorLocked(), now, mPkgList.getPackageListLocked());
+ mPkgList.forEachPackage((pkgName, holder) ->
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgName,
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ holder.appVersion)
+ );
+ if (numOfPkgs != 1) {
+ mPkgList.forEachPackageProcessStats(holder -> {
+ if (holder.state != null && holder.state != baseProcessTracker) {
+ holder.state.makeInactive();
+ }
+ });
+ mPkgList.clear();
+ ProcessStats.ProcessStateHolder holder =
+ new ProcessStats.ProcessStateHolder(info.longVersionCode);
+ tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
+ info.longVersionCode, processName);
+ mPkgList.put(info.packageName, holder);
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
}
-
}
- pkgList.clear();
- ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
- info.longVersionCode);
- tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
- info.longVersionCode, processName);
- pkgList.put(info.packageName, holder);
- if (holder.state != baseProcessTracker) {
- holder.state.makeActive();
- }
+ } else if (numOfPkgs != 1) {
+ mPkgList.clear();
+ mPkgList.put(info.packageName,
+ new ProcessStats.ProcessStateHolder(info.longVersionCode));
}
- } else if (N != 1) {
- pkgList.clear();
- pkgList.put(info.packageName,
- new ProcessStats.ProcessStateHolder(info.longVersionCode));
}
}
}
- public String[] getPackageList() {
- int size = pkgList.size();
- if (size == 0) {
- return null;
- }
- String list[] = new String[size];
- for (int i=0; i<pkgList.size(); i++) {
- list[i] = pkgList.keyAt(i);
- }
- return list;
+ String[] getPackageList() {
+ return mPkgList.getPackageList();
}
- public List<VersionedPackage> getPackageListWithVersionCode() {
- int size = pkgList.size();
- if (size == 0) {
- return null;
- }
- List<VersionedPackage> list = new ArrayList<>();
- for (int i = 0; i < pkgList.size(); i++) {
- list.add(new VersionedPackage(pkgList.keyAt(i), pkgList.valueAt(i).appVersion));
- }
- return list;
+ List<VersionedPackage> getPackageListWithVersionCode() {
+ return mPkgList.getPackageListWithVersionCode();
}
WindowProcessController getWindowProcessController() {
@@ -1221,12 +1090,12 @@
void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
- for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgList.keyAt(ipkg),
+ getPkgList().forEachPackage((pkgName, holder) ->
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgName,
ActivityManager.processStateAmToProto(mRepProcState),
- pkgList.valueAt(ipkg).appVersion);
- }
+ holder.appVersion)
+ );
mWindowProcessController.setReportedProcState(repProcState);
}
@@ -1469,8 +1338,8 @@
@Override
public void clearProfilerIfNeeded() {
- synchronized (mService) {
- mService.mAppProfiler.clearProfilerLocked();
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ mService.mAppProfiler.clearProfilerLPf();
}
}
@@ -1484,15 +1353,10 @@
@Override
public void setPendingUiClean(boolean pendingUiClean) {
synchronized (mService) {
- mPendingUiClean = pendingUiClean;
- mWindowProcessController.setPendingUiClean(pendingUiClean);
+ mProfile.setPendingUiClean(pendingUiClean);
}
}
- boolean hasPendingUiClean() {
- return mPendingUiClean;
- }
-
@Override
public void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
synchronized (mService) {
@@ -1541,7 +1405,9 @@
synchronized (mService) {
waitingToKill = null;
if (setProfileProc) {
- mService.mAppProfiler.setProfileProcLocked(this);
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ mService.mAppProfiler.setProfileProcLPf(this);
+ }
}
if (packageName != null) {
addPackage(packageName, versionCode, mService.mProcessStats);
@@ -1698,7 +1564,7 @@
// Check if package is still being loaded
boolean isPackageLoading = false;
final PackageManagerInternal packageManagerInternal =
- mService.getPackageManagerInternalLocked();
+ mService.getPackageManagerInternal();
if (aInfo != null && aInfo.packageName != null) {
IncrementalStatesInfo incrementalStatesInfo =
packageManagerInternal.getIncrementalStatesInfo(
@@ -2034,8 +1900,8 @@
}
void setAllowStartFgsByPermission() {
- boolean ret = false;
- if (!ret) {
+ int ret = FGS_FEATURE_DENIED;
+ if (ret == FGS_FEATURE_DENIED) {
boolean isSystem = false;
final int uid = UserHandle.getAppId(info.uid);
switch (uid) {
@@ -2051,18 +1917,21 @@
}
if (isSystem) {
- ret = true;
+ ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
}
}
- if (!ret) {
- for (int i = 0; i < ALLOW_BG_START_FGS_PERMISSIONS.length; ++i) {
- if (ActivityManager.checkComponentPermission(ALLOW_BG_START_FGS_PERMISSIONS[i],
- info.uid, -1, true)
- == PERMISSION_GRANTED) {
- ret = true;
- break;
- }
+ if (ret == FGS_FEATURE_DENIED) {
+ if (ActivityManager.checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND,
+ info.uid, -1, true) == PERMISSION_GRANTED) {
+ ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
+ } else if (ActivityManager.checkComponentPermission(
+ START_FOREGROUND_SERVICES_FROM_BACKGROUND,
+ info.uid, -1, true) == PERMISSION_GRANTED) {
+ ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
+ } else if (ActivityManager.checkComponentPermission(SYSTEM_ALERT_WINDOW,
+ info.uid, -1, true) == PERMISSION_GRANTED) {
+ ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
}
}
mAllowStartFgs = mAllowStartFgsByPermission = ret;
@@ -2073,38 +1942,49 @@
}
void setAllowStartFgs() {
- if (mAllowStartFgs) {
+ if (mAllowStartFgs != FGS_FEATURE_DENIED) {
return;
}
- if (!mAllowStartFgs) {
- mAllowStartFgs = isAllowedStartFgsState();
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+ if (isAllowedStartFgsState()) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+ }
}
- if (!mAllowStartFgs) {
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
// Is the calling UID a device owner app?
if (mService.mInternal != null) {
- mAllowStartFgs = mService.mInternal.isDeviceOwner(info.uid);
+ if (mService.mInternal.isDeviceOwner(info.uid)) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
+ }
}
}
- if (!mAllowStartFgs) {
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
if (mService.mInternal != null) {
- mAllowStartFgs = mService.mInternal.isAssociatedCompanionApp(
+ final boolean isCompanionApp = mService.mInternal.isAssociatedCompanionApp(
UserHandle.getUserId(info.uid), info.uid);
+ if (isCompanionApp) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
+ }
}
}
- if (!mAllowStartFgs) {
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
// Is the calling UID a profile owner app?
if (mService.mInternal != null) {
- mAllowStartFgs = mService.mInternal.isProfileOwner(info.uid);
+ if (mService.mInternal.isProfileOwner(info.uid)) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
+ }
}
}
- if (!mAllowStartFgs) {
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
// uid is on DeviceIdleController's user/system allowlist
// or AMS's FgsStartTempAllowList.
- mAllowStartFgs = mService.isWhitelistedForFgsStartLocked(info.uid);
+ if (mService.isAllowlistedForFgsStartLocked(info.uid)) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
+ }
}
}
diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
index 783f150..22e7faa 100644
--- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java
+++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
@@ -49,8 +49,8 @@
mProc = app;
mResult = result;
CharSequence name;
- if ((app.pkgList.size() == 1) &&
- (name=context.getPackageManager().getApplicationLabel(app.info)) != null) {
+ if ((app.getPkgList().size() == 1)
+ && (name = context.getPackageManager().getApplicationLabel(app.info)) != null) {
setMessage(res.getString(
com.android.internal.R.string.smv_application,
name.toString(), app.info.processName));
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index f1945ed..36d22bf 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -41,8 +41,8 @@
long lastBackgroundTime;
boolean ephemeral;
boolean foregroundServices;
- boolean curWhitelist;
- boolean setWhitelist;
+ boolean mCurAllowlist;
+ boolean mSetAllowlist;
boolean idle;
boolean setIdle;
int numProcs;
@@ -157,7 +157,7 @@
proto.write(UidRecordProto.CURRENT, ProcessList.makeProcStateProtoEnum(mCurProcState));
proto.write(UidRecordProto.EPHEMERAL, ephemeral);
proto.write(UidRecordProto.FG_SERVICES, foregroundServices);
- proto.write(UidRecordProto.WHILELIST, curWhitelist);
+ proto.write(UidRecordProto.WHILELIST, mCurAllowlist);
ProtoUtils.toDuration(proto, UidRecordProto.LAST_BACKGROUND_TIME,
lastBackgroundTime, SystemClock.elapsedRealtime());
proto.write(UidRecordProto.IDLE, idle);
@@ -191,8 +191,8 @@
if (foregroundServices) {
sb.append(" fgServices");
}
- if (curWhitelist) {
- sb.append(" whitelist");
+ if (mCurAllowlist) {
+ sb.append(" allowlist");
}
if (lastBackgroundTime > 0) {
sb.append(" bg:");
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3bbc837..6dd78e7 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -118,6 +118,7 @@
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
* Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
@@ -1458,7 +1459,7 @@
t.traceBegin("updateConfigurationAndProfileIds");
if (foreground) {
// Make sure the old user is no longer considering the display to be on.
- mInjector.reportGlobalUsageEventLocked(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
+ mInjector.reportGlobalUsageEvent(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
boolean userSwitchUiEnabled;
synchronized (mLock) {
mCurrentUserId = userId;
@@ -3045,7 +3046,7 @@
d.show();
}
- void reportGlobalUsageEventLocked(int event) {
+ void reportGlobalUsageEvent(int event) {
mService.reportGlobalUsageEvent(event);
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
new file mode 100644
index 0000000..3acad49
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.app;
+
+import android.annotation.NonNull;
+import android.app.GameManager;
+import android.app.GameManager.GameMode;
+import android.app.IGameManagerService;
+import android.content.Context;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+
+/**
+ * Service to manage game related features.
+ *
+ * <p>Game service is a core service that monitors, coordinates game related features,
+ * as well as collect metrics.</p>
+ *
+ * @hide
+ */
+public final class GameManagerService extends IGameManagerService.Stub {
+ public static final String TAG = "GameManagerService";
+
+ private static final boolean DEBUG = false;
+
+ static final int WRITE_SETTINGS = 1;
+ static final int REMOVE_SETTINGS = 2;
+ static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+ private final Handler mHandler;
+ @GuardedBy("mLock")
+ private final ArrayMap<Integer, Settings> mSettings = new ArrayMap<>();
+
+ public GameManagerService(Context context) {
+ this(context, createServiceThread().getLooper());
+ }
+
+ GameManagerService(Context context, Looper looper) {
+ mContext = context;
+ mHandler = new SettingsHandler(looper);
+ }
+
+ class SettingsHandler extends Handler {
+
+ SettingsHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ doHandleMessage(msg);
+ }
+
+ void doHandleMessage(Message msg) {
+ switch (msg.what) {
+ case WRITE_SETTINGS: {
+ final int userId = (int) msg.obj;
+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ synchronized (mLock) {
+ removeMessages(WRITE_SETTINGS, msg.obj);
+ if (mSettings.containsKey(userId)) {
+ Settings userSettings = mSettings.get(userId);
+ userSettings.writePersistentDataLocked();
+ }
+ }
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ break;
+ }
+ case REMOVE_SETTINGS: {
+ final int userId = (int) msg.obj;
+ synchronized (mLock) {
+ // Since the user was removed, ignore previous write message
+ // and do write here.
+ removeMessages(WRITE_SETTINGS, msg.obj);
+ removeMessages(REMOVE_SETTINGS, msg.obj);
+ if (mSettings.containsKey(userId)) {
+ final Settings userSettings = mSettings.get(userId);
+ mSettings.remove(userId);
+ userSettings.writePersistentDataLocked();
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * SystemService lifecycle for GameService.
+ * @hide
+ */
+ public static class Lifecycle extends SystemService {
+ private GameManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new GameManagerService(getContext());
+ publishBinderService(Context.GAME_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ mService.onBootCompleted();
+ }
+ }
+
+ @Override
+ public void onUserStarting(@NonNull TargetUser user) {
+ mService.onUserStarting(user.getUserIdentifier());
+ }
+
+ @Override
+ public void onUserStopping(@NonNull TargetUser user) {
+ mService.onUserStopping(user.getUserIdentifier());
+ }
+ }
+
+ //TODO(b/178111358) Add proper permission check and multi-user handling
+ @Override
+ public @GameMode int getGameMode(String packageName, int userId) {
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ return GameManager.GAME_MODE_UNSUPPORTED;
+ }
+ Settings userSettings = mSettings.get(userId);
+ return userSettings.getGameModeLocked(packageName);
+ }
+ }
+
+ //TODO(b/178111358) Add proper permission check and multi-user handling
+ @Override
+ public void setGameMode(String packageName, @GameMode int gameMode, int userId) {
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ return;
+ }
+ Settings userSettings = mSettings.get(userId);
+ userSettings.setGameModeLocked(packageName, gameMode);
+ final Message msg = mHandler.obtainMessage(WRITE_SETTINGS);
+ msg.obj = userId;
+ if (!mHandler.hasEqualMessages(WRITE_SETTINGS, userId)) {
+ mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
+ }
+ }
+ }
+
+ /**
+ * Notified when boot is completed.
+ */
+ @VisibleForTesting
+ void onBootCompleted() {
+ Slog.d(TAG, "onBootCompleted");
+ }
+
+ void onUserStarting(int userId) {
+ synchronized (mLock) {
+ if (mSettings.containsKey(userId)) {
+ return;
+ }
+
+ Settings userSettings = new Settings(Environment.getDataSystemDeDirectory(userId));
+ mSettings.put(userId, userSettings);
+ userSettings.readPersistentDataLocked();
+ }
+ }
+
+ void onUserStopping(int userId) {
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ return;
+ }
+ final Message msg = mHandler.obtainMessage(REMOVE_SETTINGS);
+ msg.obj = userId;
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ private static ServiceThread createServiceThread() {
+ ServiceThread handlerThread = new ServiceThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
+ handlerThread.start();
+ return handlerThread;
+ }
+}
diff --git a/services/core/java/com/android/server/app/Settings.java b/services/core/java/com/android/server/app/Settings.java
new file mode 100644
index 0000000..ab367fb
--- /dev/null
+++ b/services/core/java/com/android/server/app/Settings.java
@@ -0,0 +1,193 @@
+/*
+ * 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.app;
+
+import android.app.GameManager;
+import android.os.FileUtils;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Persists all GameService related settings.
+ * @hide
+ */
+public class Settings {
+
+ // The XML file follows the below format:
+ // <?xml>
+ // <packages>
+ // <package></package>
+ // ...
+ // </packages>
+ private static final String GAME_SERVICE_FILE_NAME = "game-manager-service.xml";
+
+ private static final String TAG_PACKAGE = "package";
+ private static final String TAG_PACKAGES = "packages";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_GAME_MODE = "gameMode";
+
+ private final File mSystemDir;
+ @VisibleForTesting
+ final AtomicFile mSettingsFile;
+
+ // PackageName -> GameMode
+ private final ArrayMap<String, Integer> mGameModes = new ArrayMap<>();
+
+ Settings(File dataDir) {
+ mSystemDir = new File(dataDir, "system");
+ mSystemDir.mkdirs();
+ FileUtils.setPermissions(mSystemDir.toString(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG
+ | FileUtils.S_IROTH | FileUtils.S_IXOTH,
+ -1, -1);
+ mSettingsFile = new AtomicFile(new File(mSystemDir, GAME_SERVICE_FILE_NAME));
+ }
+
+ /**
+ * Return the game mode of a given package.
+ * This operation must be synced with an external lock.
+ */
+ int getGameModeLocked(String packageName) {
+ if (mGameModes.containsKey(packageName)) {
+ return mGameModes.get(packageName);
+ }
+ return GameManager.GAME_MODE_UNSUPPORTED;
+ }
+
+ /**
+ * Set the game mode of a given package.
+ * This operation must be synced with an external lock.
+ */
+ void setGameModeLocked(String packageName, int gameMode) {
+ mGameModes.put(packageName, gameMode);
+ }
+
+ /**
+ * Write all current game service settings into disk.
+ * This operation must be synced with an external lock.
+ */
+ void writePersistentDataLocked() {
+ FileOutputStream fstr = null;
+ try {
+ fstr = mSettingsFile.startWrite();
+
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr);
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_PACKAGES);
+ for (Map.Entry<String, Integer> entry : mGameModes.entrySet()) {
+ serializer.startTag(null, TAG_PACKAGE);
+ serializer.attribute(null, ATTR_NAME, entry.getKey());
+ serializer.attributeInt(null, ATTR_GAME_MODE, entry.getValue());
+ serializer.endTag(null, TAG_PACKAGE);
+ }
+ serializer.endTag(null, TAG_PACKAGES);
+
+ serializer.endDocument();
+
+ mSettingsFile.finishWrite(fstr);
+
+ FileUtils.setPermissions(mSettingsFile.toString(),
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR
+ | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+ -1, -1);
+ return;
+ } catch (java.io.IOException e) {
+ mSettingsFile.failWrite(fstr);
+ Slog.wtf(GameManagerService.TAG, "Unable to write game manager service settings, "
+ + "current changes will be lost at reboot", e);
+ }
+ }
+
+ /**
+ * Read game service settings from the disk.
+ * This operation must be synced with an external lock.
+ */
+ boolean readPersistentDataLocked() {
+ mGameModes.clear();
+
+ try {
+ final FileInputStream str = mSettingsFile.openRead();
+
+ final TypedXmlPullParser parser = Xml.resolvePullParser(str);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ }
+ if (type != XmlPullParser.START_TAG) {
+ Slog.wtf(GameManagerService.TAG,
+ "No start tag found in package manager settings");
+ return false;
+ }
+
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_PACKAGE)) {
+ readPackage(parser);
+ } else {
+ Slog.w(GameManagerService.TAG, "Unknown element: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ } catch (XmlPullParserException | java.io.IOException e) {
+ Slog.wtf(GameManagerService.TAG, "Error reading package manager settings", e);
+ return false;
+ }
+
+ return true;
+ }
+
+ private void readPackage(TypedXmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ String name = null;
+ int gameMode = GameManager.GAME_MODE_UNSUPPORTED;
+ try {
+ name = parser.getAttributeValue(null, ATTR_NAME);
+ gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
+ } catch (XmlPullParserException e) {
+ Slog.wtf(GameManagerService.TAG, "Error reading game mode", e);
+ }
+ if (name != null) {
+ mGameModes.put(name, gameMode);
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 508bb01..fc48e2d 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -18,8 +18,7 @@
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-import static android.content.Intent.ACTION_USER_ADDED;
-import static android.content.Intent.ACTION_USER_REMOVED;
+import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
@@ -35,7 +34,6 @@
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
-import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -46,6 +44,8 @@
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -55,6 +55,7 @@
import java.io.FileDescriptor;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* System service that manages app hibernation state, a state apps can enter that means they are
@@ -74,6 +75,8 @@
private final UserManager mUserManager;
@GuardedBy("mLock")
private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>();
+ @GuardedBy("mLock")
+ private final Set<String> mGloballyHibernatedPackages = new ArraySet<>();
/**
* Initializes the system service.
@@ -102,11 +105,6 @@
final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ACTION_USER_ADDED);
- intentFilter.addAction(ACTION_USER_REMOVED);
- userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
-
- intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_PACKAGE_ADDED);
intentFilter.addAction(ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
@@ -118,19 +116,6 @@
publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub);
}
- @Override
- public void onBootPhase(int phase) {
- if (phase == PHASE_BOOT_COMPLETED) {
- synchronized (mLock) {
- final List<UserInfo> users = mUserManager.getUsers();
- // TODO: Pull from persistent disk storage. For now, just make from scratch.
- for (UserInfo user : users) {
- addUserPackageStatesL(user.id);
- }
- }
- }
- }
-
/**
* Whether a package is hibernating for a given user.
*
@@ -138,20 +123,34 @@
* @param userId the user to check
* @return true if package is hibernating for the user
*/
- public boolean isHibernating(String packageName, int userId) {
+ boolean isHibernatingForUser(String packageName, int userId) {
userId = handleIncomingUser(userId, "isHibernating");
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user "
+ + userId);
+ return false;
+ }
synchronized (mLock) {
final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
- if (packageStates == null) {
- throw new IllegalArgumentException("No user associated with user id " + userId);
- }
final UserPackageState pkgState = packageStates.get(packageName);
if (pkgState == null) {
throw new IllegalArgumentException(
String.format("Package %s is not installed for user %s",
packageName, userId));
}
- return pkgState != null ? pkgState.hibernated : null;
+ return pkgState.hibernated;
+ }
+ }
+
+ /**
+ * Whether a package is hibernated globally. This only occurs when a package is hibernating for
+ * all users and allows us to make optimizations at the package or APK level.
+ *
+ * @param packageName package to check
+ */
+ boolean isHibernatingGlobally(String packageName) {
+ synchronized (mLock) {
+ return mGloballyHibernatedPackages.contains(packageName);
}
}
@@ -162,12 +161,14 @@
* @param userId user
* @param isHibernating new hibernation state
*/
- public void setHibernating(String packageName, int userId, boolean isHibernating) {
+ void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
userId = handleIncomingUser(userId, "setHibernating");
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
+ + userId);
+ return;
+ }
synchronized (mLock) {
- if (!mUserStates.contains(userId)) {
- throw new IllegalArgumentException("No user associated with user id " + userId);
- }
Map<String, UserPackageState> packageStates = mUserStates.get(userId);
UserPackageState pkgState = packageStates.get(packageName);
if (pkgState == null) {
@@ -180,32 +181,99 @@
return;
}
-
- final long caller = Binder.clearCallingIdentity();
- try {
- if (isHibernating) {
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
- mIActivityManager.forceStopPackage(packageName, userId);
- mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
- null /* observer */);
- } else {
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
- mIPackageManager.setPackageStoppedState(packageName, false, userId);
- }
- pkgState.hibernated = isHibernating;
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "Failed to hibernate due to manager not being available", e);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Binder.restoreCallingIdentity(caller);
+ if (isHibernating) {
+ hibernatePackageForUserL(packageName, userId, pkgState);
+ } else {
+ unhibernatePackageForUserL(packageName, userId, pkgState);
}
-
- // TODO: Support package level hibernation when package is hibernating for all users
}
}
/**
+ * Set whether the package should be hibernated globally at a package level, allowing the
+ * the system to make optimizations at the package or APK level.
+ *
+ * @param packageName package to hibernate globally
+ * @param isHibernating new hibernation state
+ */
+ void setHibernatingGlobally(String packageName, boolean isHibernating) {
+ if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) {
+ synchronized (mLock) {
+ if (isHibernating) {
+ hibernatePackageGloballyL(packageName);
+ } else {
+ unhibernatePackageGloballyL(packageName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Put an app into hibernation for a given user, allowing user-level optimizations to occur.
+ * The caller should hold {@link #mLock}
+ *
+ * @param pkgState package hibernation state
+ */
+ private void hibernatePackageForUserL(@NonNull String packageName, int userId,
+ @NonNull UserPackageState pkgState) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mIActivityManager.forceStopPackage(packageName, userId);
+ mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
+ null /* observer */);
+ pkgState.hibernated = true;
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Failed to hibernate due to manager not being available", e);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+ }
+
+ /**
+ * Remove a package from hibernation for a given user. The caller should hold {@link #mLock}.
+ *
+ * @param pkgState package hibernation state
+ */
+ private void unhibernatePackageForUserL(@NonNull String packageName, int userId,
+ UserPackageState pkgState) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mIPackageManager.setPackageStoppedState(packageName, false, userId);
+ pkgState.hibernated = false;
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Failed to unhibernate due to manager not being available", e);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+ }
+
+ /**
+ * Put a package into global hibernation, optimizing its storage at a package / APK level.
+ * The caller should hold {@link #mLock}.
+ */
+ private void hibernatePackageGloballyL(@NonNull String packageName) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
+ // TODO(175830194): Delete vdex/odex when DexManager API is built out
+ mGloballyHibernatedPackages.add(packageName);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ /**
+ * Unhibernate a package from global hibernation. The caller should hold {@link #mLock}.
+ */
+ private void unhibernatePackageGloballyL(@NonNull String packageName) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
+ mGloballyHibernatedPackages.remove(packageName);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ /**
* Populates {@link #mUserStates} with the users installed packages. The caller should hold
* {@link #mLock}.
*
@@ -220,21 +288,25 @@
throw new IllegalStateException("Package manager not available.", e);
}
- for (PackageInfo pkg : packageList) {
- packages.put(pkg.packageName, new UserPackageState());
+ for (int i = 0, size = packageList.size(); i < size; i++) {
+ packages.put(packageList.get(i).packageName, new UserPackageState());
}
mUserStates.put(userId, packages);
}
- private void onUserAdded(int userId) {
+ @Override
+ public void onUserUnlocking(@NonNull TargetUser user) {
+ // TODO: Pull from persistent disk storage. For now, just make from scratch.
synchronized (mLock) {
- addUserPackageStatesL(userId);
+ addUserPackageStatesL(user.getUserIdentifier());
}
}
- private void onUserRemoved(int userId) {
+ @Override
+ public void onUserStopping(@NonNull TargetUser user) {
synchronized (mLock) {
- mUserStates.remove(userId);
+ // TODO: Flush to disk when persistence is implemented
+ mUserStates.remove(user.getUserIdentifier());
}
}
@@ -250,6 +322,12 @@
}
}
+ private void onPackageRemovedForAllUsers(@NonNull String packageName) {
+ synchronized (mLock) {
+ mGloballyHibernatedPackages.remove(packageName);
+ }
+ }
+
/**
* Private helper method to get the real user id and enforce permission checks.
*
@@ -277,13 +355,23 @@
}
@Override
- public boolean isHibernating(String packageName, int userId) {
- return mService.isHibernating(packageName, userId);
+ public boolean isHibernatingForUser(String packageName, int userId) {
+ return mService.isHibernatingForUser(packageName, userId);
}
@Override
- public void setHibernating(String packageName, int userId, boolean isHibernating) {
- mService.setHibernating(packageName, userId, isHibernating);
+ public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
+ mService.setHibernatingForUser(packageName, userId, isHibernating);
+ }
+
+ @Override
+ public void setHibernatingGlobally(String packageName, boolean isHibernating) {
+ mService.setHibernatingGlobally(packageName, isHibernating);
+ }
+
+ @Override
+ public boolean isHibernatingGlobally(String packageName) {
+ return mService.isHibernatingGlobally(packageName);
}
@Override
@@ -295,7 +383,7 @@
}
}
- // Broadcast receiver for user and package add/removal events
+ // Broadcast receiver for package add/removal events
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -305,12 +393,6 @@
}
final String action = intent.getAction();
- if (ACTION_USER_ADDED.equals(action)) {
- onUserAdded(userId);
- }
- if (ACTION_USER_REMOVED.equals(action)) {
- onUserRemoved(userId);
- }
if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
final String packageName = intent.getData().getSchemeSpecificPart();
if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {
@@ -322,6 +404,9 @@
onPackageAdded(packageName, userId);
} else if (ACTION_PACKAGE_REMOVED.equals(action)) {
onPackageRemoved(packageName, userId);
+ if (intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false)) {
+ onPackageRemovedForAllUsers(packageName);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
index 869885e..7d6eea2 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
@@ -18,7 +18,6 @@
import android.os.ShellCommand;
import android.os.UserHandle;
-import android.text.TextUtils;
import java.io.PrintWriter;
@@ -27,6 +26,7 @@
*/
final class AppHibernationShellCommand extends ShellCommand {
private static final String USER_OPT = "--user";
+ private static final String GLOBAL_OPT = "--global";
private static final int SUCCESS = 0;
private static final int ERROR = -1;
private final AppHibernationService mService;
@@ -51,7 +51,21 @@
}
private int runSetState() {
- int userId = parseUserOption();
+ String opt;
+ boolean setsGlobal = false;
+ int userId = UserHandle.USER_CURRENT;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case USER_OPT:
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ case GLOBAL_OPT:
+ setsGlobal = true;
+ break;
+ default:
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ }
+ }
String pkg = getNextArgRequired();
if (pkg == null) {
@@ -66,32 +80,43 @@
}
boolean newState = Boolean.parseBoolean(newStateRaw);
- mService.setHibernating(pkg, userId, newState);
+ if (setsGlobal) {
+ mService.setHibernatingGlobally(pkg, newState);
+ } else {
+ mService.setHibernatingForUser(pkg, userId, newState);
+ }
return SUCCESS;
}
private int runGetState() {
- int userId = parseUserOption();
+ String opt;
+ boolean requestsGlobal = false;
+ int userId = UserHandle.USER_CURRENT;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case USER_OPT:
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ case GLOBAL_OPT:
+ requestsGlobal = true;
+ break;
+ default:
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ }
+ }
String pkg = getNextArgRequired();
if (pkg == null) {
getErrPrintWriter().println("Error: No package specified");
return ERROR;
}
- boolean isHibernating = mService.isHibernating(pkg, userId);
+ boolean isHibernating = requestsGlobal
+ ? mService.isHibernatingGlobally(pkg) : mService.isHibernatingForUser(pkg, userId);
final PrintWriter pw = getOutPrintWriter();
pw.println(isHibernating);
return SUCCESS;
}
- private int parseUserOption() {
- String option = getNextOption();
- if (TextUtils.equals(option, USER_OPT)) {
- return UserHandle.parseUserArg(getNextArgRequired());
- }
- return UserHandle.USER_CURRENT;
- }
-
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -99,11 +124,13 @@
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
- pw.println(" set-state [--user USER_ID] PACKAGE true|false");
- pw.println(" Sets the hibernation state of the package to value specified");
+ pw.println(" set-state [--user USER_ID] [--global] PACKAGE true|false");
+ pw.println(" Sets the hibernation state of the package to value specified. Optionally");
+ pw.println(" may specify a user id or set global hibernation state.");
pw.println("");
- pw.println(" get-state [--user USER_ID] PACKAGE");
- pw.println(" Gets the hibernation state of the package");
+ pw.println(" get-state [--user USER_ID] [--global] PACKAGE");
+ pw.println(" Gets the hibernation state of the package. Optionally may specify a user");
+ pw.println(" id or request global hibernation state.");
pw.println("");
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d575963..a43f0c6 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1076,7 +1076,6 @@
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
synchronized (mHdmiClientLock) {
- mHdmiCecSink = false;
mHdmiManager = mContext.getSystemService(HdmiControlManager.class);
if (mHdmiManager != null) {
mHdmiManager.addHdmiControlStatusChangeListener(
@@ -1508,7 +1507,8 @@
if (isPlatformTelevision()) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null && mHdmiPlaybackClient != null) {
- updateHdmiCecSinkLocked(mHdmiCecSink | false);
+ updateHdmiCecSinkLocked(
+ mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI));
}
}
}
@@ -1518,7 +1518,8 @@
if (isPlatformTelevision()) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
- updateHdmiCecSinkLocked(mHdmiCecSink | false);
+ updateHdmiCecSinkLocked(
+ mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI));
}
}
}
@@ -2670,8 +2671,7 @@
if (adjustVolume) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
- // mHdmiCecSink true => mHdmiPlaybackClient != null
- if (mHdmiCecSink
+ if (mHdmiPlaybackClient != null
&& mHdmiCecVolumeControlEnabled
&& streamTypeAlias == AudioSystem.STREAM_MUSIC
// vol change on a full volume device
@@ -7825,9 +7825,8 @@
@GuardedBy("mHdmiClientLock")
private void updateHdmiCecSinkLocked(boolean hdmiCecSink) {
- mHdmiCecSink = hdmiCecSink;
if (!hasDeviceVolumeBehavior(AudioSystem.DEVICE_OUT_HDMI)) {
- if (mHdmiCecSink) {
+ if (hdmiCecSink) {
if (DEBUG_VOL) {
Log.d(TAG, "CEC sink: setting HDMI as full vol device");
}
@@ -7885,9 +7884,6 @@
// Set only when device is a set-top box.
@GuardedBy("mHdmiClientLock")
private HdmiPlaybackClient mHdmiPlaybackClient;
- // true if we are a set-top box, an HDMI sink is connected and it supports CEC.
- @GuardedBy("mHdmiClientLock")
- private boolean mHdmiCecSink;
// Set only when device is an audio system.
@GuardedBy("mHdmiClientLock")
private HdmiAudioSystemClient mHdmiAudioSystemClient;
@@ -8142,7 +8138,6 @@
pw.print(" mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
pw.print(" mFullVolumeDevices="); pw.println(dumpDeviceTypes(mFullVolumeDevices));
pw.print(" mExtVolumeController="); pw.println(mExtVolumeController);
- pw.print(" mHdmiCecSink="); pw.println(mHdmiCecSink);
pw.print(" mHdmiAudioSystemClient="); pw.println(mHdmiAudioSystemClient);
pw.print(" mHdmiPlaybackClient="); pw.println(mHdmiPlaybackClient);
pw.print(" mHdmiTvClient="); pw.println(mHdmiTvClient);
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 271537a..c5237ab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -635,10 +635,15 @@
proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null
? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE);
proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled);
- Slog.d(getTag(), "Total operations: " + mTotalOperationsHandled);
- for (int i = 0; i < mRecentOperations.size(); i++) {
- Slog.d(getTag(), "Operation: " + mRecentOperations.get(i));
- proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, mRecentOperations.get(i));
+
+ if (!mRecentOperations.isEmpty()) {
+ for (int i = 0; i < mRecentOperations.size(); i++) {
+ proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, mRecentOperations.get(i));
+ }
+ } else {
+ // TODO:(b/178828362) Unsure why protobuf has a problem decoding when an empty list
+ // is returned. So, let's just add a no-op for this case.
+ proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, BiometricsProto.CM_NONE);
}
proto.flush();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index d3b5b5a..303c080 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -114,11 +114,8 @@
* Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to
* be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
*/
- default void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
- @NonNull IInvalidationCallback callback) {
- throw new IllegalStateException("Providers that support invalidation must override"
- + " this method");
- }
+ void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
+ @NonNull IInvalidationCallback callback);
long getAuthenticatorId(int sensorId, int userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index a4a8401..9869f77 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -28,6 +28,7 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
@@ -144,11 +145,11 @@
private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback =
new LockoutFrameworkImpl.LockoutResetCallback() {
- @Override
- public void onLockoutReset(int userId) {
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
- }
- };
+ @Override
+ public void onLockoutReset(int userId) {
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
+ }
+ };
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
@@ -355,7 +356,7 @@
final int maxEnrollmentsPerUser = mContext.getResources()
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
- mSensorProperties = new FingerprintSensorPropertiesInternal(sensorId,
+ mSensorProperties = new FingerprintSensorPropertiesInternal(context, sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
sensorType, resetLockoutRequiresHardwareAuthToken);
}
@@ -630,13 +631,13 @@
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
@NonNull String opPackageName) {
mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
+ scheduleUpdateActiveUserWithoutHandler(userId);
- final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
- userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client);
+ final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
+ userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
+ mSensorProperties.sensorId, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
});
}
@@ -780,6 +781,17 @@
}
@Override
+ public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
+ @NonNull IInvalidationCallback callback) {
+ // TODO (b/179101888): Remove this temporary workaround.
+ try {
+ callback.onCompleted();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to complete InvalidateAuthenticatorId");
+ }
+ }
+
+ @Override
public void dumpInternal(int sensorId, @NonNull PrintWriter pw) {
PerformanceTracker performanceTracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b282484..1a4f20c7 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -57,6 +57,7 @@
import com.android.server.ConnectivityService;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
@@ -1025,6 +1026,8 @@
+ (networkAgentConfig.acceptUnvalidated ? " acceptUnvalidated" : "")
+ (networkAgentConfig.acceptPartialConnectivity ? " acceptPartialConnectivity" : "")
+ (clatd.isStarted() ? " clat{" + clatd + "} " : "")
+ + (declaredUnderlyingNetworks != null
+ ? " underlying{" + Arrays.toString(declaredUnderlyingNetworks) + "}" : "")
+ " lp{" + linkProperties + "}"
+ " nc{" + networkCapabilities + "}"
+ "}";
diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
index 5dc8c1a..aadaf4d 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
@@ -16,7 +16,6 @@
package com.android.server.connectivity;
-import android.annotation.NonNull;
import android.annotation.WorkerThread;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -72,6 +71,10 @@
private static final int DELAY_LONG = 4;
private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
+ // Return values for #setCurrentProxyScriptUrl
+ public static final boolean DONT_SEND_BROADCAST = false;
+ public static final boolean DO_SEND_BROADCAST = true;
+
private String mCurrentPac;
@GuardedBy("mProxyLock")
private volatile Uri mPacUrl = Uri.EMPTY;
@@ -90,7 +93,7 @@
private volatile boolean mHasSentBroadcast;
private volatile boolean mHasDownloaded;
- private final Handler mConnectivityHandler;
+ private Handler mConnectivityHandler;
private final int mProxyMessage;
/**
@@ -99,13 +102,6 @@
private final Object mProxyLock = new Object();
/**
- * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
- * last URL and port, and the broadcast message being sent with the correct arguments.
- * TODO : this should probably protect all instances of these variables
- */
- private final Object mBroadcastStateLock = new Object();
-
- /**
* Runnable to download PAC script.
* The behavior relies on the assumption it always runs on mNetThread to guarantee that the
* latest data fetched from mPacUrl is stored in mProxyService.
@@ -150,7 +146,7 @@
}
}
- public PacProxyInstaller(@NonNull Context context, @NonNull Handler handler, int proxyMessage) {
+ public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
mContext = context;
mLastPort = -1;
final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
@@ -180,27 +176,31 @@
* PacProxyInstaller will trigger a new broadcast when it is ready.
*
* @param proxy Proxy information that is about to be broadcast.
+ * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
*/
- public void setCurrentProxyScriptUrl(@NonNull ProxyInfo proxy) {
- synchronized (mBroadcastStateLock) {
- if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
- if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
- mPacUrl = proxy.getPacFileUrl();
- mCurrentDelay = DELAY_1;
- mHasSentBroadcast = false;
- mHasDownloaded = false;
- getAlarmManager().cancel(mPacRefreshIntent);
- bind();
- } else {
- getAlarmManager().cancel(mPacRefreshIntent);
- synchronized (mProxyLock) {
- mPacUrl = Uri.EMPTY;
- mCurrentPac = null;
- if (mProxyService != null) {
- unbind();
- }
+ public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+ if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+ if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
+ // Allow to send broadcast, nothing to do.
+ return DO_SEND_BROADCAST;
+ }
+ mPacUrl = proxy.getPacFileUrl();
+ mCurrentDelay = DELAY_1;
+ mHasSentBroadcast = false;
+ mHasDownloaded = false;
+ getAlarmManager().cancel(mPacRefreshIntent);
+ bind();
+ return DONT_SEND_BROADCAST;
+ } else {
+ getAlarmManager().cancel(mPacRefreshIntent);
+ synchronized (mProxyLock) {
+ mPacUrl = Uri.EMPTY;
+ mCurrentPac = null;
+ if (mProxyService != null) {
+ unbind();
}
}
+ return DO_SEND_BROADCAST;
}
}
@@ -275,7 +275,6 @@
getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
}
- @GuardedBy("mProxyLock")
private void setCurrentProxyScript(String script) {
if (mProxyService == null) {
Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -348,9 +347,6 @@
public void setProxyPort(int port) {
if (mLastPort != -1) {
// Always need to send if port changed
- // TODO: Here lacks synchronization because this write cannot
- // guarantee that it's visible from sendProxyIfNeeded() when
- // it's called by a Runnable which is post by mNetThread.
mHasSentBroadcast = false;
}
mLastPort = port;
@@ -390,15 +386,13 @@
mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
}
- private void sendProxyIfNeeded() {
- synchronized (mBroadcastStateLock) {
- if (!mHasDownloaded || (mLastPort == -1)) {
- return;
- }
- if (!mHasSentBroadcast) {
- sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
- mHasSentBroadcast = true;
- }
+ private synchronized void sendProxyIfNeeded() {
+ if (!mHasDownloaded || (mLastPort == -1)) {
+ return;
+ }
+ if (!mHasSentBroadcast) {
+ sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+ mHasSentBroadcast = true;
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index b618d2b..d83ff83 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -226,9 +226,9 @@
final ProxyInfo defaultProxy = getDefaultProxy();
final ProxyInfo proxyInfo = null != defaultProxy ?
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
- mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo);
- if (!shouldSendBroadcast(proxyInfo)) {
+ if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
+ == PacProxyInstaller.DONT_SEND_BROADCAST) {
return;
}
if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,13 +244,6 @@
}
}
- private boolean shouldSendBroadcast(ProxyInfo proxy) {
- if (Uri.EMPTY.equals(proxy.getPacFileUrl())) return false;
- if (proxy.getPacFileUrl().equals(proxy.getPacFileUrl())
- && (proxy.getPort() > 0)) return true;
- return true;
- }
-
/**
* Sets the global proxy in memory. Also writes the values to the global settings of the device.
*
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 8ce6746..d956ba3 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -70,7 +70,7 @@
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.UidRangeParcel;
-import android.net.VpnInfo;
+import android.net.UnderlyingNetworkInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -426,6 +426,7 @@
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
+ mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
loadAlwaysOnPackage(keyStore);
}
@@ -1819,12 +1820,12 @@
* This method should not be called if underlying interfaces field is needed, because it doesn't
* have enough data to fill VpnInfo.underlyingIfaces field.
*/
- public synchronized VpnInfo getVpnInfo() {
+ public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() {
if (!isRunningLocked()) {
return null;
}
- return new VpnInfo(mOwnerUID, mInterface, null);
+ return new UnderlyingNetworkInfo(mOwnerUID, mInterface, new ArrayList<>());
}
public synchronized boolean appliesToUid(int uid) {
@@ -1981,27 +1982,28 @@
* secondary thread to perform connection work, returning quickly.
*
* Should only be called to respond to Binder requests as this enforces caller permission. Use
- * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
+ * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, Network, LinkProperties)} to skip the
* permission check only when the caller is trusted (or the call is initiated by the system).
*/
- public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
+ public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, @Nullable Network underlying,
+ LinkProperties egress) {
enforceControlPermission();
final long token = Binder.clearCallingIdentity();
try {
- startLegacyVpnPrivileged(profile, keyStore, egress);
+ startLegacyVpnPrivileged(profile, keyStore, underlying, egress);
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
- * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
- * permissions under the assumption that the caller is the system.
+ * Like {@link #startLegacyVpn(VpnProfile, KeyStore, Network, LinkProperties)}, but does not
+ * check permissions under the assumption that the caller is the system.
*
* Callers are responsible for checking permissions if needed.
*/
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
- LinkProperties egress) {
+ @Nullable Network underlying, @NonNull LinkProperties egress) {
UserManager mgr = UserManager.get(mContext);
UserInfo user = mgr.getUserInfo(mUserId);
if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
@@ -2127,6 +2129,9 @@
config.session = profile.name;
config.isMetered = false;
config.proxyInfo = profile.proxy;
+ if (underlying != null) {
+ config.underlyingNetworks = new Network[] { underlying };
+ }
config.addLegacyRoutes(profile.routes);
if (!profile.dnsServers.isEmpty()) {
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
new file mode 100644
index 0000000..802472f
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -0,0 +1,86 @@
+/*
+ * 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.devicestate;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A state of the device defined by the {@link DeviceStateProvider} and managed by the
+ * {@link DeviceStateManagerService}.
+ * <p>
+ * Device state is an abstract concept that allows mapping the current state of the device to the
+ * state of the system. This is useful for variable-state devices, like foldable or rollable
+ * devices, that can be configured by users into differing hardware states, which each may have a
+ * different expected use case.
+ *
+ * @see DeviceStateProvider
+ * @see DeviceStateManagerService
+ */
+public final class DeviceState {
+ /** Unique identifier for the device state. */
+ @IntRange(from = INVALID_DEVICE_STATE)
+ private final int mIdentifier;
+
+ /** String description of the device state. */
+ @NonNull
+ private final String mName;
+
+ public DeviceState(@IntRange(from = INVALID_DEVICE_STATE) int identifier,
+ @NonNull String name) {
+ if (identifier != INVALID_DEVICE_STATE && identifier < 0) {
+ throw new IllegalArgumentException("Identifier must be greater than or equal to zero.");
+ }
+ mIdentifier = identifier;
+ mName = name;
+ }
+
+ /** Returns the unique identifier for the device state. */
+ @IntRange(from = INVALID_DEVICE_STATE)
+ public int getIdentifier() {
+ return mIdentifier;
+ }
+
+ /** Returns a string description of the device state. */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DeviceState that = (DeviceState) o;
+ return mIdentifier == that.mIdentifier
+ && Objects.equals(mName, that.mName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIdentifier, mName);
+ }
+}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index d7dcbde..375ec3a 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,7 +19,9 @@
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.devicestate.IDeviceStateManager;
@@ -29,7 +31,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
-import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -42,7 +43,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Optional;
/**
* A system service that manages the state of a device with user-configurable hardware like a
@@ -74,26 +75,31 @@
@NonNull
private final BinderService mBinderService;
+ // All supported device states keyed by identifier.
@GuardedBy("mLock")
- private IntArray mSupportedDeviceStates;
+ private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
- // The current committed device state.
+ // The current committed device state. The default of INVALID_DEVICE_STATE will be replaced by
+ // the current state after the initial callback from the DeviceStateProvider.
@GuardedBy("mLock")
- private int mCommittedState = INVALID_DEVICE_STATE;
+ @NonNull
+ private DeviceState mCommittedState = new DeviceState(INVALID_DEVICE_STATE, "INVALID");
// The device state that is currently awaiting callback from the policy to be committed.
@GuardedBy("mLock")
- private int mPendingState = INVALID_DEVICE_STATE;
+ @NonNull
+ private Optional<DeviceState> mPendingState = Optional.empty();
// Whether or not the policy is currently waiting to be notified of the current pending state.
@GuardedBy("mLock")
private boolean mIsPolicyWaitingForState = false;
// The device state that is currently requested and is next to be configured and committed.
// Can be overwritten by an override state value if requested.
@GuardedBy("mLock")
- private int mRequestedState = INVALID_DEVICE_STATE;
- // The most recently requested override state, or INVALID_DEVICE_STATE if no override is
- // requested.
+ @NonNull
+ private Optional<DeviceState> mRequestedState = Optional.empty();
+ // The most recently requested override state, or empty if no override is requested.
@GuardedBy("mLock")
- private int mRequestedOverrideState = INVALID_DEVICE_STATE;
+ @NonNull
+ private Optional<DeviceState> mRequestedOverrideState = Optional.empty();
// List of registered callbacks indexed by process id.
@GuardedBy("mLock")
@@ -122,18 +128,20 @@
*
* @see #getPendingState()
*/
- int getCommittedState() {
+ @NonNull
+ DeviceState getCommittedState() {
synchronized (mLock) {
return mCommittedState;
}
}
/**
- * Returns the state the system is currently configuring, or {@link #INVALID_DEVICE_STATE} if
- * the system is not in the process of configuring a state.
+ * Returns the state the system is currently configuring, or {@link Optional#empty()} if the
+ * system is not in the process of configuring a state.
*/
@VisibleForTesting
- int getPendingState() {
+ @NonNull
+ Optional<DeviceState> getPendingState() {
synchronized (mLock) {
return mPendingState;
}
@@ -143,7 +151,8 @@
* Returns the requested state. The service will configure the device to match the requested
* state when possible.
*/
- int getRequestedState() {
+ @NonNull
+ Optional<DeviceState> getRequestedState() {
synchronized (mLock) {
return mRequestedState;
}
@@ -165,7 +174,7 @@
return false;
}
- mRequestedOverrideState = overrideState;
+ mRequestedOverrideState = getStateLocked(overrideState);
updatePendingStateLocked();
}
@@ -181,20 +190,24 @@
}
/**
- * Returns the current requested override state, or {@link #INVALID_DEVICE_STATE} is no override
+ * Returns the current requested override state, or {@link Optional#empty()} if no override
* state is requested.
*/
- int getOverrideState() {
+ @NonNull
+ Optional<DeviceState> getOverrideState() {
synchronized (mLock) {
return mRequestedOverrideState;
}
}
/** Returns the list of currently supported device states. */
- int[] getSupportedStates() {
+ DeviceState[] getSupportedStates() {
synchronized (mLock) {
- // Copy array to prevent external modification of internal state.
- return Arrays.copyOf(mSupportedDeviceStates.toArray(), mSupportedDeviceStates.size());
+ DeviceState[] supportedStates = new DeviceState[mDeviceStates.size()];
+ for (int i = 0; i < supportedStates.length; i++) {
+ supportedStates[i] = mDeviceStates.valueAt(i);
+ }
+ return supportedStates;
}
}
@@ -203,24 +216,26 @@
return mBinderService;
}
- private void updateSupportedStates(int[] supportedDeviceStates) {
- // Must ensure sorted as isSupportedStateLocked() impl uses binary search.
- Arrays.sort(supportedDeviceStates, 0, supportedDeviceStates.length);
+ private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
synchronized (mLock) {
- mSupportedDeviceStates = IntArray.wrap(supportedDeviceStates);
+ mDeviceStates.clear();
+ for (int i = 0; i < supportedDeviceStates.length; i++) {
+ DeviceState state = supportedDeviceStates[i];
+ mDeviceStates.put(state.getIdentifier(), state);
+ }
- if (mRequestedState != INVALID_DEVICE_STATE
- && !isSupportedStateLocked(mRequestedState)) {
+ if (mRequestedState.isPresent()
+ && !isSupportedStateLocked(mRequestedState.get().getIdentifier())) {
// The current requested state is no longer valid. We'll clear it here, though
// we won't actually update the current state until a callback comes from the
// provider with the most recent state.
- mRequestedState = INVALID_DEVICE_STATE;
+ mRequestedState = Optional.empty();
}
- if (mRequestedOverrideState != INVALID_DEVICE_STATE
- && !isSupportedStateLocked(mRequestedOverrideState)) {
+ if (mRequestedOverrideState.isPresent()
+ && !isSupportedStateLocked(mRequestedOverrideState.get().getIdentifier())) {
// The current override state is no longer valid. We'll clear it here and update
// the committed state if necessary.
- mRequestedOverrideState = INVALID_DEVICE_STATE;
+ mRequestedOverrideState = Optional.empty();
}
updatePendingStateLocked();
}
@@ -230,10 +245,19 @@
/**
* Returns {@code true} if the provided state is supported. Requires that
- * {@link #mSupportedDeviceStates} is sorted prior to calling.
+ * {@link #mDeviceStates} is sorted prior to calling.
*/
- private boolean isSupportedStateLocked(int state) {
- return mSupportedDeviceStates.binarySearch(state) >= 0;
+ private boolean isSupportedStateLocked(int identifier) {
+ return mDeviceStates.contains(identifier);
+ }
+
+ /**
+ * Returns the {@link DeviceState} with the supplied {@code identifier}, or {@code null} if
+ * there is no device state with the identifier.
+ */
+ @Nullable
+ private Optional<DeviceState> getStateLocked(int identifier) {
+ return Optional.ofNullable(mDeviceStates.get(identifier));
}
/**
@@ -242,10 +266,11 @@
*
* @see #isSupportedStateLocked(int)
*/
- private void requestState(int state) {
+ private void requestState(int identifier) {
synchronized (mLock) {
- if (isSupportedStateLocked(state)) {
- mRequestedState = state;
+ final Optional<DeviceState> requestedState = getStateLocked(identifier);
+ if (requestedState.isPresent()) {
+ mRequestedState = requestedState;
}
updatePendingStateLocked();
}
@@ -259,19 +284,19 @@
* changed.
*/
private void updatePendingStateLocked() {
- if (mPendingState != INVALID_DEVICE_STATE) {
+ if (mPendingState.isPresent()) {
// Have pending state, can not configure a new state until the state is committed.
return;
}
- int stateToConfigure;
- if (mRequestedOverrideState != INVALID_DEVICE_STATE) {
- stateToConfigure = mRequestedOverrideState;
+ final DeviceState stateToConfigure;
+ if (mRequestedOverrideState.isPresent()) {
+ stateToConfigure = mRequestedOverrideState.get();
} else {
- stateToConfigure = mRequestedState;
+ stateToConfigure = mRequestedState.orElse(null);
}
- if (stateToConfigure == INVALID_DEVICE_STATE) {
+ if (stateToConfigure == null) {
// No currently requested state.
return;
}
@@ -281,7 +306,7 @@
return;
}
- mPendingState = stateToConfigure;
+ mPendingState = Optional.of(stateToConfigure);
mIsPolicyWaitingForState = true;
}
@@ -302,7 +327,7 @@
return;
}
mIsPolicyWaitingForState = false;
- state = mPendingState;
+ state = mPendingState.get().getIdentifier();
}
if (DEBUG) {
@@ -333,9 +358,9 @@
if (DEBUG) {
Slog.d(TAG, "Committing state: " + mPendingState);
}
- mCommittedState = mPendingState;
- newState = mCommittedState;
- mPendingState = INVALID_DEVICE_STATE;
+ mCommittedState = mPendingState.get();
+ newState = mCommittedState.getIdentifier();
+ mPendingState = Optional.empty();
updatePendingStateLocked();
}
@@ -389,7 +414,7 @@
}
mCallbacks.put(callingPid, record);
- currentState = mCommittedState;
+ currentState = mCommittedState.getIdentifier();
}
// Notify the callback of the state at registration.
@@ -406,10 +431,10 @@
pw.println("DEVICE STATE MANAGER (dumpsys device_state)");
synchronized (mLock) {
- pw.println(" mCommittedState=" + toString(mCommittedState));
- pw.println(" mPendingState=" + toString(mPendingState));
- pw.println(" mRequestedState=" + toString(mRequestedState));
- pw.println(" mRequestedOverrideState=" + toString(mRequestedOverrideState));
+ pw.println(" mCommittedState=" + mCommittedState);
+ pw.println(" mPendingState=" + mPendingState);
+ pw.println(" mRequestedState=" + mRequestedState);
+ pw.println(" mRequestedOverrideState=" + mRequestedOverrideState);
final int callbackCount = mCallbacks.size();
pw.println();
@@ -421,30 +446,28 @@
}
}
- private String toString(int state) {
- return state == INVALID_DEVICE_STATE ? "(none)" : String.valueOf(state);
- }
-
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
@Override
- public void onSupportedDeviceStatesChanged(int[] newDeviceStates) {
+ public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
+ if (newDeviceStates.length == 0) {
+ throw new IllegalArgumentException("Supported device states must not be empty");
+ }
for (int i = 0; i < newDeviceStates.length; i++) {
- if (newDeviceStates[i] < 0) {
- throw new IllegalArgumentException("Supported device states includes invalid"
- + " value: " + newDeviceStates[i]);
+ if (newDeviceStates[i].getIdentifier() == INVALID_DEVICE_STATE) {
+ throw new IllegalArgumentException(
+ "Supported device states includes INVALID_DEVICE_STATE identifier");
}
}
-
updateSupportedStates(newDeviceStates);
}
@Override
- public void onStateChanged(int state) {
- if (state < 0) {
- throw new IllegalArgumentException("Invalid state: " + state);
+ public void onStateChanged(@IntRange(from = 0) int identifier) {
+ if (identifier < 0) {
+ throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
- requestState(state);
+ requestState(identifier);
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index cf3b297..7914531 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,11 +16,10 @@
package com.android.server.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-
import android.os.ShellCommand;
import java.io.PrintWriter;
+import java.util.Optional;
/**
* ShellCommands for {@link DeviceStateManagerService}.
@@ -52,24 +51,15 @@
}
private void printState(PrintWriter pw) {
- int committedState = mInternal.getCommittedState();
- int requestedState = mInternal.getRequestedState();
- int requestedOverrideState = mInternal.getOverrideState();
+ DeviceState committedState = mInternal.getCommittedState();
+ Optional<DeviceState> requestedState = mInternal.getRequestedState();
+ Optional<DeviceState> requestedOverrideState = mInternal.getOverrideState();
- if (committedState == INVALID_DEVICE_STATE) {
- pw.println("Device state: (invalid)");
- } else {
- pw.println("Device state: " + committedState);
- }
-
- if (requestedOverrideState != INVALID_DEVICE_STATE) {
+ pw.println("Committed state: " + committedState);
+ if (requestedOverrideState.isPresent()) {
pw.println("----------------------");
- if (requestedState == INVALID_DEVICE_STATE) {
- pw.println("Base state: (invalid)");
- } else {
- pw.println("Base state: " + requestedState);
- }
- pw.println("Override state: " + committedState);
+ pw.println("Base state: " + requestedState.orElse(null));
+ pw.println("Override state: " + requestedOverrideState.get());
}
}
@@ -102,15 +92,12 @@
}
private int runPrintStates(PrintWriter pw) {
- int[] states = mInternal.getSupportedStates();
- pw.print("Supported states: [ ");
+ DeviceState[] states = mInternal.getSupportedStates();
+ pw.print("Supported states: [\n");
for (int i = 0; i < states.length; i++) {
- pw.print(states[i]);
- if (i < states.length - 1) {
- pw.print(", ");
- }
+ pw.print(" " + states[i] + ",\n");
}
- pw.println(" ]");
+ pw.println("]");
return 0;
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 0e8bf9b..2d4377f 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -16,9 +16,11 @@
package com.android.server.devicestate;
+import android.annotation.IntRange;
+
/**
- * Responsible for providing the set of currently supported device states and well as the current
- * device state.
+ * Responsible for providing the set of supported {@link DeviceState device states} as well as the
+ * current device state.
*
* @see DeviceStatePolicy
*/
@@ -26,8 +28,8 @@
/**
* Registers a listener for changes in provider state.
* <p>
- * It is <b>required</b> that {@link Listener#onSupportedDeviceStatesChanged(int[])} be called
- * followed by {@link Listener#onStateChanged(int)} with the initial values on successful
+ * It is <b>required</b> that {@link Listener#onSupportedDeviceStatesChanged(DeviceState[])} be
+ * called followed by {@link Listener#onStateChanged(int)} with the initial values on successful
* registration of the listener.
*/
void setListener(Listener listener);
@@ -35,35 +37,36 @@
/** Callback for changes in {@link DeviceStateProvider} state. */
interface Listener {
/**
- * Called to notify the listener of a change in supported device states. Required to be
- * called once on successful registration of the listener and then once on every
- * subsequent change in supported device states.
+ * Called to notify the listener of a change in supported {@link DeviceState device states}.
+ * Required to be called once on successful registration of the listener and then once on
+ * every subsequent change in supported device states.
* <p>
* The set of device states can change based on the current hardware state of the device.
* For example, if a device state depends on a particular peripheral device (display, etc)
* it would only be reported as supported when the device is plugged. Otherwise, it should
* not be included in the set of supported states.
* <p>
- * All values provided must be greater than or equal to zero and there must always be at
- * least one supported device state.
+ * The identifier for every provided device state must be unique and greater than or equal
+ * to zero and there must always be at least one supported device state.
*
* @param newDeviceStates array of supported device states.
*
* @throws IllegalArgumentException if the list of device states is empty or if one of the
- * provided states is less than 0.
+ * provided states contains an invalid identifier.
*/
- void onSupportedDeviceStatesChanged(int[] newDeviceStates);
+ void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates);
/**
* Called to notify the listener of a change in current device state. Required to be called
* once on successful registration of the listener and then once on every subsequent change
* in device state. Value must have been included in the set of supported device states
- * provided in the most recent call to {@link #onSupportedDeviceStatesChanged(int[])}.
+ * provided in the most recent call to
+ * {@link #onSupportedDeviceStatesChanged(DeviceState[])}.
*
- * @param state the new device state.
+ * @param identifier the identifier of the new device state.
*
* @throws IllegalArgumentException if the state is less than 0.
*/
- void onStateChanged(int state);
+ void onStateChanged(@IntRange(from = 0) int identifier);
}
}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index b8e579d..4832e46 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -300,6 +300,8 @@
/** @return The default brightness configuration. */
public abstract BrightnessConfiguration getDefaultConfig();
+ /** Recalculates the backlight-to-nits and nits-to-backlight splines. */
+ public abstract void recalculateSplines(boolean applyAdjustment, float[] adjustment);
/**
* Returns the timeout for the short term model
@@ -658,6 +660,11 @@
}
@Override
+ public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
+ // Do nothing.
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("SimpleMappingStrategy");
pw.println(" mSpline=" + mSpline);
@@ -696,7 +703,7 @@
// A spline mapping from nits to the corresponding backlight value, normalized to the range
// [0, 1.0].
- private final Spline mNitsToBacklightSpline;
+ private Spline mNitsToBacklightSpline;
// The default brightness configuration.
private final BrightnessConfiguration mDefaultConfig;
@@ -705,6 +712,11 @@
// a brightness in nits.
private Spline mBacklightToNitsSpline;
+ private float[] mNits;
+ private int[] mBacklight;
+
+ private boolean mBrightnessRangeAdjustmentApplied;
+
private float mMaxGamma;
private float mAutoBrightnessAdjustment;
private float mUserLux;
@@ -726,15 +738,9 @@
mUserLux = -1;
mUserBrightness = -1;
- // Setup the backlight spline
- final int N = nits.length;
- float[] normalizedBacklight = new float[N];
- for (int i = 0; i < N; i++) {
- normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
- }
-
- mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
- mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
+ mNits = nits;
+ mBacklight = backlight;
+ computeNitsBrightnessSplines(mNits);
mDefaultConfig = config;
if (mLoggingEnabled) {
@@ -868,6 +874,12 @@
}
@Override
+ public void recalculateSplines(boolean applyAdjustment, float[] adjustedNits) {
+ mBrightnessRangeAdjustmentApplied = applyAdjustment;
+ computeNitsBrightnessSplines(mBrightnessRangeAdjustmentApplied ? adjustedNits : mNits);
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("PhysicalMappingStrategy");
pw.println(" mConfig=" + mConfig);
@@ -878,6 +890,18 @@
pw.println(" mUserLux=" + mUserLux);
pw.println(" mUserBrightness=" + mUserBrightness);
pw.println(" mDefaultConfig=" + mDefaultConfig);
+ pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
+ }
+
+ private void computeNitsBrightnessSplines(float[] nits) {
+ final int len = nits.length;
+ float[] normalizedBacklight = new float[len];
+ for (int i = 0; i < len; i++) {
+ normalizedBacklight[i] = normalizeAbsoluteBrightness(mBacklight[i]);
+ }
+
+ mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
+ mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
}
private void computeSpline() {
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 2375f74..a186e33 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -63,7 +63,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.RingBuffer;
import com.android.server.LocalServices;
@@ -71,7 +70,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
@@ -80,7 +78,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -118,6 +115,9 @@
private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
private static final String ATTR_NIGHT_MODE = "nightMode";
private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
+ private static final String ATTR_REDUCE_BRIGHT_COLORS = "reduceBrightColors";
+ private static final String ATTR_REDUCE_BRIGHT_COLORS_STRENGTH = "reduceBrightColorsStrength";
+ private static final String ATTR_REDUCE_BRIGHT_COLORS_OFFSET = "reduceBrightColorsOffset";
private static final String ATTR_LAST_NITS = "lastNits";
private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
private static final String ATTR_POWER_SAVE = "powerSaveFactor";
@@ -398,6 +398,10 @@
builder.setNightMode(mInjector.isNightDisplayActivated(mContext));
builder.setColorTemperature(mInjector.getNightDisplayColorTemperature(mContext));
+ builder.setReduceBrightColors(mInjector.isReduceBrightColorsActivated(mContext));
+ builder.setReduceBrightColorsStrength(mInjector.getReduceBrightColorsStrength(mContext));
+ builder.setReduceBrightColorsOffset(mInjector.getReduceBrightColorsOffsetFactor(mContext)
+ * brightness);
if (mColorSamplingEnabled) {
DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample);
@@ -563,6 +567,12 @@
out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode);
out.attributeInt(null, ATTR_COLOR_TEMPERATURE,
toWrite[i].colorTemperature);
+ out.attributeBoolean(null, ATTR_REDUCE_BRIGHT_COLORS,
+ toWrite[i].reduceBrightColors);
+ out.attributeInt(null, ATTR_REDUCE_BRIGHT_COLORS_STRENGTH,
+ toWrite[i].reduceBrightColorsStrength);
+ out.attributeFloat(null, ATTR_REDUCE_BRIGHT_COLORS_OFFSET,
+ toWrite[i].reduceBrightColorsOffset);
out.attributeFloat(null, ATTR_LAST_NITS,
toWrite[i].lastBrightness);
out.attributeBoolean(null, ATTR_DEFAULT_CONFIG,
@@ -641,6 +651,12 @@
builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE));
builder.setColorTemperature(
parser.getAttributeInt(null, ATTR_COLOR_TEMPERATURE));
+ builder.setReduceBrightColors(
+ parser.getAttributeBoolean(null, ATTR_REDUCE_BRIGHT_COLORS));
+ builder.setReduceBrightColorsStrength(
+ parser.getAttributeInt(null, ATTR_REDUCE_BRIGHT_COLORS_STRENGTH));
+ builder.setReduceBrightColorsOffset(
+ parser.getAttributeFloat(null, ATTR_REDUCE_BRIGHT_COLORS_OFFSET));
builder.setLastBrightness(parser.getAttributeFloat(null, ATTR_LAST_NITS));
String luxValue = parser.getAttributeValue(null, ATTR_LUX);
@@ -1114,6 +1130,21 @@
return context.getSystemService(ColorDisplayManager.class).isNightDisplayActivated();
}
+ public int getReduceBrightColorsStrength(Context context) {
+ return context.getSystemService(ColorDisplayManager.class)
+ .getReduceBrightColorsStrength();
+ }
+
+ public float getReduceBrightColorsOffsetFactor(Context context) {
+ return context.getSystemService(ColorDisplayManager.class)
+ .getReduceBrightColorsOffsetFactor();
+ }
+
+ public boolean isReduceBrightColorsActivated(Context context) {
+ return context.getSystemService(ColorDisplayManager.class)
+ .isReduceBrightColorsActivated();
+ }
+
public DisplayedContentSample sampleColor(int noFramesToSample) {
final DisplayManagerInternal displayManagerInternal =
LocalServices.getService(DisplayManagerInternal.class);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 2641ee7..bf16a6d 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -23,6 +23,7 @@
import android.view.DisplayAddress;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
+import android.view.RoundedCorners;
import android.view.Surface;
import com.android.internal.BrightnessSynchronizer;
@@ -281,6 +282,11 @@
public DisplayCutout displayCutout;
/**
+ * The {@link RoundedCorners} if present or {@code null} otherwise.
+ */
+ public RoundedCorners roundedCorners;
+
+ /**
* The touch attachment, per {@link DisplayViewport#touch}.
*/
public int touch;
@@ -401,7 +407,8 @@
|| !BrightnessSynchronizer.floatEquals(brightnessMinimum, other.brightnessMinimum)
|| !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
|| !BrightnessSynchronizer.floatEquals(brightnessDefault,
- other.brightnessDefault)) {
+ other.brightnessDefault)
+ || !Objects.equals(roundedCorners, other.roundedCorners)) {
diff |= DIFF_OTHER;
}
return diff;
@@ -444,6 +451,7 @@
brightnessMinimum = other.brightnessMinimum;
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
+ roundedCorners = other.roundedCorners;
}
// For debugging purposes
@@ -487,6 +495,9 @@
sb.append(", brightnessMinimum ").append(brightnessMinimum);
sb.append(", brightnessMaximum ").append(brightnessMaximum);
sb.append(", brightnessDefault ").append(brightnessDefault);
+ if (roundedCorners != null) {
+ sb.append(", roundedCorners ").append(roundedCorners);
+ }
sb.append(flagsToString(flags));
sb.append("}");
return sb.toString();
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index a11a745..663883a 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -21,7 +21,8 @@
/**
* Represents a collection of {@link LogicalDisplay}s which act in unison for certain behaviors and
- * operations.
+ * operations; particularly display-state.
+ *
* @hide
*/
public class DisplayGroup {
@@ -33,17 +34,44 @@
mGroupId = groupId;
}
+ /** Returns the identifier for the Group. */
int getGroupId() {
return mGroupId;
}
- void addDisplay(LogicalDisplay display) {
+ /**
+ * Adds the provided {@code display} to the Group
+ *
+ * @param display the {@link LogicalDisplay} to add to the Group
+ */
+ void addDisplayLocked(LogicalDisplay display) {
if (!mDisplays.contains(display)) {
mDisplays.add(display);
}
}
- boolean removeDisplay(LogicalDisplay display) {
+ /**
+ * Removes the provided {@code display} from the Group.
+ *
+ * @param display The {@link LogicalDisplay} to remove from the Group.
+ * @return {@code true} if the {@code display} was removed; otherwise {@code false}
+ */
+ boolean removeDisplayLocked(LogicalDisplay display) {
return mDisplays.remove(display);
}
+
+ /** Returns {@code true} if there are no {@link LogicalDisplay LogicalDisplays} in the Group. */
+ boolean isEmptyLocked() {
+ return mDisplays.isEmpty();
+ }
+
+ /** Returns the number of {@link LogicalDisplay LogicalDisplays} in the Group. */
+ int getSizeLocked() {
+ return mDisplays.size();
+ }
+
+ /** Returns the ID of the {@link LogicalDisplay} at the provided {@code index}. */
+ int getIdLocked(int index) {
+ return mDisplays.get(index).getDisplayIdLocked();
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c3f8d8c..97753c8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -57,6 +57,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
@@ -190,6 +191,7 @@
private static final int MSG_UPDATE_VIEWPORT = 5;
private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6;
private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7;
+ private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8;
private final Context mContext;
private final DisplayManagerHandler mHandler;
@@ -237,6 +239,10 @@
private final CopyOnWriteArrayList<DisplayTransactionListener> mDisplayTransactionListeners =
new CopyOnWriteArrayList<DisplayTransactionListener>();
+ /** List of all display group listeners. */
+ private final CopyOnWriteArrayList<DisplayGroupListener> mDisplayGroupListeners =
+ new CopyOnWriteArrayList<>();
+
/** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */
private final SparseArray<DisplayPowerController> mDisplayPowerControllers =
new SparseArray<>();
@@ -1677,6 +1683,11 @@
mHandler.sendMessage(msg);
}
+ private void sendDisplayGroupEvent(int groupId, int event) {
+ Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_GROUP_EVENT, groupId, event);
+ mHandler.sendMessage(msg);
+ }
+
private void sendDisplayEventFrameRateOverrideLocked(int displayId) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
@@ -1721,6 +1732,35 @@
mTempCallbacks.clear();
}
+ // Runs on Handler thread.
+ // Delivers display group event notifications to callbacks.
+ private void deliverDisplayGroupEvent(int groupId, int event) {
+ if (DEBUG) {
+ Slog.d(TAG, "Delivering display group event: groupId=" + groupId + ", event="
+ + event);
+ }
+
+ switch (event) {
+ case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED:
+ for (DisplayGroupListener listener : mDisplayGroupListeners) {
+ listener.onDisplayGroupAdded(groupId);
+ }
+ break;
+
+ case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED:
+ for (DisplayGroupListener listener : mDisplayGroupListeners) {
+ listener.onDisplayGroupChanged(groupId);
+ }
+ break;
+
+ case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED:
+ for (DisplayGroupListener listener : mDisplayGroupListeners) {
+ listener.onDisplayGroupRemoved(groupId);
+ }
+ break;
+ }
+ }
+
private IMediaProjectionManager getProjectionService() {
if (mProjectionService == null) {
IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
@@ -1943,6 +1983,11 @@
}
deliverDisplayEvent(msg.arg1, uids, msg.arg2);
break;
+
+ case MSG_DELIVER_DISPLAY_GROUP_EVENT:
+ deliverDisplayGroupEvent(msg.arg1, msg.arg2);
+ break;
+
}
}
}
@@ -1974,6 +2019,11 @@
}
@Override
+ public void onDisplayGroupEventLocked(int groupId, int event) {
+ sendDisplayGroupEvent(groupId, event);
+ }
+
+ @Override
public void onTraversalRequested() {
synchronized (mSyncRoot) {
scheduleTraversalLocked(false);
@@ -2708,7 +2758,7 @@
}
@Override
- public boolean requestPowerState(DisplayPowerRequest request,
+ public boolean requestPowerState(int groupId, DisplayPowerRequest request,
boolean waitForNegativeProximity) {
synchronized (mSyncRoot) {
return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
@@ -2732,6 +2782,16 @@
}
@Override
+ public void registerDisplayGroupListener(DisplayGroupListener listener) {
+ mDisplayGroupListeners.add(listener);
+ }
+
+ @Override
+ public void unregisterDisplayGroupListener(DisplayGroupListener listener) {
+ mDisplayGroupListeners.remove(listener);
+ }
+
+ @Override
public SurfaceControl.ScreenshotHardwareBuffer systemScreenshot(int displayId) {
return systemScreenshotInternal(displayId);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 13dc0b9..8c65158 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -58,6 +58,8 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
+import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
@@ -346,6 +348,9 @@
@Nullable
private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
+ private final ColorDisplayServiceInternal mCdsi;
+ private final float[] mNitsRange;
+
// A record of state for skipping brightness ramps.
private int mSkipRampState = RAMP_STATE_SKIP_NONE;
@@ -580,6 +585,37 @@
}
mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
mDisplayWhiteBalanceController = displayWhiteBalanceController;
+
+ if (displayDeviceConfig != null && displayDeviceConfig.getNits() != null) {
+ mNitsRange = displayDeviceConfig.getNits();
+ } else {
+ Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back");
+ 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();
+ }
+
+ @Override
+ public void onReduceBrightColorsStrengthChanged(int strength) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ });
+ if (active) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ }
+
+ private void applyReduceBrightColorsSplineAdjustment() {
+ float[] adjustedNits = new float[mNitsRange.length];
+ for (int i = 0; i < mNitsRange.length; i++) {
+ adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
+ }
+ mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits);
}
private Sensor findDisplayLightSensor(String sensorType) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 8198e51..7e6a137 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -35,6 +35,7 @@
import android.view.DisplayAddress;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
+import android.view.RoundedCorners;
import android.view.SurfaceControl;
import com.android.internal.BrightnessSynchronizer;
@@ -604,6 +605,8 @@
}
mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
mInfo.width, mInfo.height);
+ mInfo.roundedCorners = RoundedCorners.fromResources(
+ res, mInfo.width, mInfo.height);
} else {
if (!res.getBoolean(
com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 86de159..80781d2 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -195,6 +195,7 @@
info.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
+ info.roundedCorners = mOverrideDisplayInfo.roundedCorners;
}
mInfo.set(info);
}
@@ -386,6 +387,7 @@
mBaseDisplayInfo.brightnessMinimum = deviceInfo.brightnessMinimum;
mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
mBaseDisplayInfo.brightnessDefault = deviceInfo.brightnessDefault;
+ mBaseDisplayInfo.roundedCorners = deviceInfo.roundedCorners;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index e738878..16c4b26 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -55,6 +55,10 @@
public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4;
public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5;
+ public static final int DISPLAY_GROUP_EVENT_ADDED = 1;
+ public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
+ public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;
+
/**
* Temporary display info, used for comparing display configurations.
*/
@@ -99,11 +103,11 @@
private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
/** A mapping from logical display id to display group. */
- private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
+ private final SparseArray<DisplayGroup> mDisplayIdToGroupMap = new SparseArray<>();
private final DisplayDeviceRepository mDisplayDeviceRepo;
private final Listener mListener;
- private final int mFoldedDeviceState;
+ private final int[] mFoldedDeviceStates;
LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener) {
mDisplayDeviceRepo = repo;
@@ -111,8 +115,8 @@
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
mDisplayDeviceRepo.addListener(this);
- mFoldedDeviceState = context.getResources().getInteger(
- com.android.internal.R.integer.config_foldedDeviceState);
+ mFoldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
loadFoldedDisplayConfig(context);
}
@@ -183,7 +187,7 @@
}
public int getDisplayGroupIdLocked(int displayId) {
- final DisplayGroup displayGroup = mDisplayGroups.get(displayId);
+ final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId);
if (displayGroup != null) {
return displayGroup.getGroupId();
}
@@ -191,6 +195,18 @@
return -1;
}
+ public DisplayGroup getDisplayGroupLocked(int groupId) {
+ final int size = mDisplayIdToGroupMap.size();
+ for (int i = 0; i < size; i++) {
+ final DisplayGroup displayGroup = mDisplayIdToGroupMap.valueAt(i);
+ if (displayGroup.getGroupId() == groupId) {
+ return displayGroup;
+ }
+ }
+
+ return null;
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.println("LogicalDisplayMapper:");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
@@ -216,7 +232,14 @@
}
void setDeviceStateLocked(int state) {
- setDeviceFoldedLocked(state == mFoldedDeviceState);
+ boolean folded = false;
+ for (int i = 0; i < mFoldedDeviceStates.length; i++) {
+ if (state == mFoldedDeviceStates[i]) {
+ folded = true;
+ break;
+ }
+ }
+ setDeviceFoldedLocked(folded);
}
void setDeviceFoldedLocked(boolean isFolded) {
@@ -320,7 +343,7 @@
final int groupId = assignDisplayGroupIdLocked(isDefault);
displayGroup = new DisplayGroup(groupId);
} else {
- displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY);
+ displayGroup = mDisplayIdToGroupMap.get(Display.DEFAULT_DISPLAY);
}
LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
@@ -335,11 +358,23 @@
mLogicalDisplays.put(displayId, display);
- displayGroup.addDisplay(display);
- mDisplayGroups.append(displayId, displayGroup);
+ displayGroup.addDisplayLocked(display);
+ mDisplayIdToGroupMap.append(displayId, displayGroup);
+
+ if (addNewDisplayGroup) {
+ // Group added events happen before Logical Display added events.
+ mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+ LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
+ }
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED);
+
+ if (!addNewDisplayGroup) {
+ // Group changed events happen after Logical Display added events.
+ mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+ LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
+ }
}
/**
@@ -356,33 +391,47 @@
DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides =
display.getFrameRateOverrides();
display.updateLocked(mDisplayDeviceRepo);
+ final DisplayGroup changedDisplayGroup;
if (!display.isValidLocked()) {
mLogicalDisplays.removeAt(i);
- mDisplayGroups.removeReturnOld(displayId).removeDisplay(display);
+ final DisplayGroup displayGroup = mDisplayIdToGroupMap.removeReturnOld(displayId);
+ displayGroup.removeDisplayLocked(display);
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED);
+
+ changedDisplayGroup = displayGroup;
} else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
final int flags = display.getDisplayInfoLocked().flags;
- final DisplayGroup defaultDisplayGroup = mDisplayGroups.get(
+ final DisplayGroup defaultDisplayGroup = mDisplayIdToGroupMap.get(
Display.DEFAULT_DISPLAY);
if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) {
// The display should have its own DisplayGroup.
- if (defaultDisplayGroup.removeDisplay(display)) {
+ if (defaultDisplayGroup.removeDisplayLocked(display)) {
final int groupId = assignDisplayGroupIdLocked(false);
final DisplayGroup displayGroup = new DisplayGroup(groupId);
- displayGroup.addDisplay(display);
- mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup);
+ displayGroup.addDisplayLocked(display);
display.updateDisplayGroupIdLocked(groupId);
+ mDisplayIdToGroupMap.append(display.getDisplayIdLocked(), displayGroup);
+ mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
+ LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
+ changedDisplayGroup = defaultDisplayGroup;
+ } else {
+ changedDisplayGroup = null;
}
} else {
// The display should be a part of the default DisplayGroup.
- final DisplayGroup displayGroup = mDisplayGroups.get(displayId);
+ final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId);
if (displayGroup != defaultDisplayGroup) {
- displayGroup.removeDisplay(display);
- defaultDisplayGroup.addDisplay(display);
- mDisplayGroups.put(displayId, defaultDisplayGroup);
+ displayGroup.removeDisplayLocked(display);
+ defaultDisplayGroup.addDisplayLocked(display);
display.updateDisplayGroupIdLocked(defaultDisplayGroup.getGroupId());
+ mListener.onDisplayGroupEventLocked(defaultDisplayGroup.getGroupId(),
+ LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
+ mDisplayIdToGroupMap.put(displayId, defaultDisplayGroup);
+ changedDisplayGroup = displayGroup;
+ } else {
+ changedDisplayGroup = null;
}
}
@@ -394,6 +443,7 @@
} else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
+ changedDisplayGroup = null;
} else {
// While applications shouldn't know nor care about the non-overridden info, we
// still need to let WindowManager know so it can update its own internal state for
@@ -403,6 +453,15 @@
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED);
}
+ changedDisplayGroup = null;
+ }
+
+ // CHANGED and REMOVED DisplayGroup events should always fire after Display events.
+ if (changedDisplayGroup != null) {
+ final int event = changedDisplayGroup.isEmptyLocked()
+ ? LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED
+ : LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED;
+ mListener.onDisplayGroupEventLocked(changedDisplayGroup.getGroupId(), event);
}
}
}
@@ -438,6 +497,7 @@
public interface Listener {
void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
+ void onDisplayGroupEventLocked(int groupId, int event);
void onTraversalRequested();
}
}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 4706edc..88b2668 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -173,6 +173,7 @@
private ContentObserver mContentObserver;
private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
+ private ReduceBrightColorsListener mReduceBrightColorsListener;
private NightDisplayAutoMode mNightDisplayAutoMode;
@@ -617,18 +618,24 @@
if (mCurrentUser == UserHandle.USER_NULL) {
return;
}
- mReduceBrightColorsTintController.setActivated(
- Secure.getIntForUser(getContext().getContentResolver(),
- Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1);
+ final boolean activated = Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1;
+ mReduceBrightColorsTintController.setActivated(activated);
+ if (mReduceBrightColorsListener != null) {
+ mReduceBrightColorsListener.onReduceBrightColorsActivationChanged(activated);
+ }
}
private void onReduceBrightColorsStrengthLevelChanged() {
if (mCurrentUser == UserHandle.USER_NULL) {
return;
}
- mReduceBrightColorsTintController.setMatrix(
- Secure.getIntForUser(getContext().getContentResolver(),
- Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mCurrentUser));
+ final int strength = Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mCurrentUser);
+ mReduceBrightColorsTintController.setMatrix(strength);
+ if (mReduceBrightColorsListener != null) {
+ mReduceBrightColorsListener.onReduceBrightColorsStrengthChanged(strength);
+ }
}
/**
@@ -762,6 +769,22 @@
mCurrentUser) == 1;
}
+ private boolean setReduceBrightColorsActivatedInternal(boolean activated) {
+ if (mCurrentUser == UserHandle.USER_NULL) {
+ return false;
+ }
+ return Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, activated ? 1 : 0, mCurrentUser);
+ }
+
+ private boolean setReduceBrightColorsStrengthInternal(int strength) {
+ if (mCurrentUser == UserHandle.USER_NULL) {
+ return false;
+ }
+ return Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.REDUCE_BRIGHT_COLORS_LEVEL, strength, mCurrentUser);
+ }
+
private boolean isDeviceColorManagedInternal() {
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
return dtm.isDeviceColorManaged();
@@ -1469,6 +1492,31 @@
}
/**
+ * Sets the listener and returns whether reduce bright colors is currently enabled.
+ */
+ public boolean setReduceBrightColorsListener(ReduceBrightColorsListener listener) {
+ mReduceBrightColorsListener = listener;
+ return mReduceBrightColorsTintController.isActivated();
+ }
+
+ /**
+ * Returns whether reduce bright colors is currently active.
+ */
+ public boolean isReduceBrightColorsActivated() {
+ return mReduceBrightColorsTintController.isActivated();
+ }
+
+ /**
+ * Gets the computed brightness, in nits, when the reduce bright colors feature is applied
+ * at the current strength.
+ *
+ * @hide
+ */
+ public float getReduceBrightColorsAdjustedBrightnessNits(float nits) {
+ return mReduceBrightColorsTintController.getAdjustedBrightness(nits);
+ }
+
+ /**
* Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
* invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
*/
@@ -1491,6 +1539,22 @@
void onDisplayWhiteBalanceStatusChanged(boolean activated);
}
+ /**
+ * Listener for changes in reduce bright colors status.
+ */
+ public interface ReduceBrightColorsListener {
+
+ /**
+ * Notify that the reduce bright colors activation status has changed.
+ */
+ void onReduceBrightColorsActivationChanged(boolean activated);
+
+ /**
+ * Notify that the reduce bright colors strength has changed.
+ */
+ void onReduceBrightColorsStrengthChanged(int strength);
+ }
+
private final class TintHandler extends Handler {
private TintHandler(Looper looper) {
@@ -1787,6 +1851,62 @@
}
@Override
+ public boolean isReduceBrightColorsActivated() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mReduceBrightColorsTintController.isActivated();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean setReduceBrightColorsActivated(boolean activated) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set reduce bright colors activation state");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return setReduceBrightColorsActivatedInternal(activated);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public int getReduceBrightColorsStrength() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mReduceBrightColorsTintController.getStrength();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public float getReduceBrightColorsOffsetFactor() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mReduceBrightColorsTintController.getOffsetFactor();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean setReduceBrightColorsStrength(int strength) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set reduce bright colors strength");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return setReduceBrightColorsStrengthInternal(strength);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
diff --git a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
index 7e120c9..cb93cc8 100644
--- a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
+++ b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
@@ -34,7 +34,7 @@
public class ReduceBrightColorsTintController extends TintController {
private final float[] mMatrix = new float[16];
- private final float[] mCoefficients = new float[9];
+ private final float[] mCoefficients = new float[3];
private int mStrength;
@@ -42,8 +42,8 @@
public void setUp(Context context, boolean needsLinear) {
final String[] coefficients = context.getResources().getStringArray(
needsLinear ? R.array.config_reduceBrightColorsCoefficients
- : R.array.config_reduceBrightColorsCoefficientsNative);
- for (int i = 0; i < 9 && i < coefficients.length; i++) {
+ : R.array.config_reduceBrightColorsCoefficientsNonlinear);
+ for (int i = 0; i < 3 && i < coefficients.length; i++) {
mCoefficients[i] = Float.parseFloat(coefficients[i]);
}
}
@@ -67,20 +67,11 @@
Matrix.setIdentityM(mMatrix, 0);
- final float percentageStrength = strengthLevel / 100f;
- final float squaredPercentageStrength = percentageStrength * percentageStrength;
- final float red =
- squaredPercentageStrength * mCoefficients[0] + percentageStrength * mCoefficients[1]
- + mCoefficients[2];
- final float green =
- squaredPercentageStrength * mCoefficients[3] + percentageStrength * mCoefficients[4]
- + mCoefficients[5];
- final float blue =
- squaredPercentageStrength * mCoefficients[6] + percentageStrength * mCoefficients[7]
- + mCoefficients[8];
- mMatrix[0] = clamp(red);
- mMatrix[5] = clamp(green);
- mMatrix[10] = clamp(blue);
+ // All three (r,g,b) components are equal and calculated with the same formula.
+ final float componentValue = computeComponentValue(strengthLevel);
+ mMatrix[0] = componentValue;
+ mMatrix[5] = componentValue;
+ mMatrix[10] = componentValue;
}
private float clamp(float value) {
@@ -110,4 +101,26 @@
public int getStrength() {
return mStrength;
}
+
+ /** Returns the offset factor at Ymax. */
+ public float getOffsetFactor() {
+ // Strength terms drop out as strength --> 1, leaving the coefficients.
+ return mCoefficients[0] + mCoefficients[1] + mCoefficients[2];
+ }
+
+ /**
+ * Returns the effective brightness (in nits), which has been adjusted to account for the effect
+ * of the bright color reduction.
+ */
+ public float getAdjustedBrightness(float nits) {
+ return computeComponentValue(mStrength) * nits;
+ }
+
+ private float computeComponentValue(int strengthLevel) {
+ final float percentageStrength = strengthLevel / 100f;
+ final float squaredPercentageStrength = percentageStrength * percentageStrength;
+ return clamp(
+ squaredPercentageStrength * mCoefficients[0] + percentageStrength * mCoefficients[1]
+ + mCoefficients[2]);
+ }
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontCrashDetector.java b/services/core/java/com/android/server/graphics/fonts/FontCrashDetector.java
new file mode 100644
index 0000000..b082b25
--- /dev/null
+++ b/services/core/java/com/android/server/graphics/fonts/FontCrashDetector.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.server.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A class to detect font-related native crash.
+ *
+ * <p>If a fs-verity protected file is accessed through mmap and corrupted file block is detected,
+ * SIGBUG signal is generated and the process will crash. To find corrupted files and remove them,
+ * we use a marker file to detect crash.
+ * <ol>
+ * <li>Create a marker file before reading fs-verity protected font files.
+ * <li>Delete the marker file after reading font files successfully.
+ * <li>If the marker file is found in the next process startup, it means that the process
+ * crashed before. We will delete font files to prevent crash loop.
+ * </ol>
+ *
+ * <p>Example usage:
+ * <pre>
+ * FontCrashDetector detector = new FontCrashDetector(new File("/path/to/marker_file"));
+ * if (detector.hasCrashed()) {
+ * // Do cleanup
+ * }
+ * try (FontCrashDetector.MonitoredBlock b = detector.start()) {
+ * // Read files
+ * }
+ * </pre>
+ *
+ * <p>This class DOES NOT detect Java exceptions. If a Java exception is thrown while monitoring
+ * crash, the marker file will be deleted. Creating and deleting marker files are not lightweight.
+ * Please use this class sparingly with caution.
+ */
+/* package */ final class FontCrashDetector {
+
+ private static final String TAG = "FontCrashDetector";
+
+ @NonNull
+ private final File mMarkerFile;
+
+ /* package */ FontCrashDetector(@NonNull File markerFile) {
+ mMarkerFile = markerFile;
+ }
+
+ /* package */ boolean hasCrashed() {
+ return mMarkerFile.exists();
+ }
+
+ /* package */ void clear() {
+ if (!mMarkerFile.delete()) {
+ Slog.e(TAG, "Could not delete marker file: " + mMarkerFile);
+ }
+ }
+
+ /** Starts crash monitoring. */
+ /* package */ MonitoredBlock start() {
+ try {
+ mMarkerFile.createNewFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not create marker file: " + mMarkerFile, e);
+ }
+ return new MonitoredBlock();
+ }
+
+ /** A helper class to monitor crash with try-with-resources syntax. */
+ /* package */ class MonitoredBlock implements AutoCloseable {
+ /** Ends crash monitoring. */
+ @Override
+ public void close() {
+ clear();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 5f7d938..9036812 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.graphics.fonts;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -24,7 +25,7 @@
import android.graphics.fonts.FontFileUtil;
import android.graphics.fonts.FontManager;
import android.graphics.fonts.SystemFonts;
-import android.os.RemoteException;
+import android.os.ParcelFileDescriptor;
import android.os.ResultReceiver;
import android.os.SharedMemory;
import android.os.ShellCallback;
@@ -37,6 +38,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.graphics.fonts.IFontManager;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.security.FileIntegrityService;
@@ -53,6 +55,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
/** A service for managing system fonts. */
// TODO(b/173619554): Add API to update fonts.
@@ -60,16 +63,33 @@
private static final String TAG = "FontManagerService";
private static final String FONT_FILES_DIR = "/data/fonts/files";
+ private static final String CRASH_MARKER_FILE = "/data/fonts/config/crash.txt";
@Override
- public FontConfig getFontConfig() throws RemoteException {
+ public FontConfig getFontConfig() {
return getSystemFontConfig();
}
+ @Override
+ public int updateFont(ParcelFileDescriptor fd, byte[] signature, int baseVersion) {
+ Objects.requireNonNull(fd);
+ Objects.requireNonNull(signature);
+ Preconditions.checkArgumentNonnegative(baseVersion);
+ getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
+ "UPDATE_FONTS permission required.");
+ try {
+ installFontFile(fd.getFileDescriptor(), signature, baseVersion);
+ return FontManager.RESULT_SUCCESS;
+ } catch (SystemFontException e) {
+ Slog.e(TAG, "Failed to update font file", e);
+ return e.getErrorCode();
+ }
+ }
+
/* package */ static class SystemFontException extends AndroidException {
private final int mErrorCode;
- SystemFontException(@FontManager.ErrorCode int errorCode, String msg, Throwable cause) {
+ SystemFontException(@FontManager.ResultCode int errorCode, String msg, Throwable cause) {
super(msg, cause);
mErrorCode = errorCode;
}
@@ -79,7 +99,8 @@
mErrorCode = errorCode;
}
- @FontManager.ErrorCode int getErrorCode() {
+ @FontManager.ResultCode
+ int getErrorCode() {
return mErrorCode;
}
}
@@ -157,16 +178,32 @@
}
}
+ @NonNull
+ private final Context mContext;
+
+ private final Object mUpdatableFontDirLock = new Object();
+
+ @GuardedBy("mUpdatableFontDirLock")
+ @NonNull
+ private final FontCrashDetector mFontCrashDetector;
+
+ @GuardedBy("mUpdatableFontDirLock")
@Nullable
private final UpdatableFontDir mUpdatableFontDir;
- @GuardedBy("FontManagerService.this")
+ // mSerializedFontMapLock can be acquired while holding mUpdatableFontDirLock.
+ // mUpdatableFontDirLock should not be newly acquired while holding mSerializedFontMapLock.
+ private final Object mSerializedFontMapLock = new Object();
+
+ @GuardedBy("mSerializedFontMapLock")
@Nullable
private SharedMemory mSerializedFontMap = null;
private FontManagerService(Context context) {
mContext = context;
+ mFontCrashDetector = new FontCrashDetector(new File(CRASH_MARKER_FILE));
mUpdatableFontDir = createUpdatableFontDir();
+ initialize();
}
@Nullable
@@ -179,51 +216,80 @@
new OtfFontFileParser(), new FsverityUtilImpl());
}
-
- @NonNull
- private final Context mContext;
+ private void initialize() {
+ synchronized (mUpdatableFontDirLock) {
+ if (mUpdatableFontDir == null) {
+ updateSerializedFontMap();
+ return;
+ }
+ if (mFontCrashDetector.hasCrashed()) {
+ Slog.i(TAG, "Crash detected. Clearing font updates.");
+ try {
+ mUpdatableFontDir.clearUpdates();
+ } catch (SystemFontException e) {
+ Slog.e(TAG, "Failed to clear updates.", e);
+ }
+ mFontCrashDetector.clear();
+ }
+ try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
+ mUpdatableFontDir.loadFontFileMap();
+ updateSerializedFontMap();
+ }
+ }
+ }
@NonNull
public Context getContext() {
return mContext;
}
- @NonNull /* package */ SharedMemory getCurrentFontMap() {
- synchronized (FontManagerService.this) {
- if (mSerializedFontMap == null) {
- mSerializedFontMap = buildNewSerializedFontMap();
- }
+ @Nullable /* package */ SharedMemory getCurrentFontMap() {
+ synchronized (mSerializedFontMapLock) {
return mSerializedFontMap;
}
}
- /* package */ void installFontFile(FileDescriptor fd, byte[] pkcs7Signature)
+ /* package */ void installFontFile(FileDescriptor fd, byte[] pkcs7Signature, int baseVersion)
throws SystemFontException {
if (mUpdatableFontDir == null) {
throw new SystemFontException(
- FontManager.ERROR_CODE_FONT_UPDATER_DISABLED,
+ FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
"The font updater is disabled.");
}
- synchronized (FontManagerService.this) {
- mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
- // Create updated font map in the next getSerializedSystemFontMap() call.
- mSerializedFontMap = null;
+ synchronized (mUpdatableFontDirLock) {
+ // baseVersion == -1 only happens from shell command. This is filtered and treated as
+ // error from SystemApi call.
+ if (baseVersion != -1 && mUpdatableFontDir.getConfigVersion() != baseVersion) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_VERSION_MISMATCH,
+ "The base config version is older than current.");
+ }
+ try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
+ mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
+ updateSerializedFontMap();
+ }
}
}
/* package */ void clearUpdates() throws SystemFontException {
if (mUpdatableFontDir == null) {
throw new SystemFontException(
- FontManager.ERROR_CODE_FONT_UPDATER_DISABLED,
+ FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
"The font updater is disabled.");
}
- mUpdatableFontDir.clearUpdates();
+ synchronized (mUpdatableFontDirLock) {
+ try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
+ mUpdatableFontDir.clearUpdates();
+ updateSerializedFontMap();
+ }
+ }
}
/* package */ Map<String, File> getFontFileMap() {
if (mUpdatableFontDir == null) {
return Collections.emptyMap();
- } else {
+ }
+ synchronized (mUpdatableFontDirLock) {
return mUpdatableFontDir.getFontFileMap();
}
}
@@ -241,7 +307,7 @@
@Nullable FileDescriptor err,
@NonNull String[] args,
@Nullable ShellCallback callback,
- @NonNull ResultReceiver result) throws RemoteException {
+ @NonNull ResultReceiver result) {
new FontManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
}
@@ -249,24 +315,28 @@
* Returns an active system font configuration.
*/
public @NonNull FontConfig getSystemFontConfig() {
- if (mUpdatableFontDir != null) {
- return mUpdatableFontDir.getSystemFontConfig();
- } else {
+ if (mUpdatableFontDir == null) {
return SystemFonts.getSystemPreinstalledFontConfig();
}
+ synchronized (mUpdatableFontDirLock) {
+ return mUpdatableFontDir.getSystemFontConfig();
+ }
}
/**
- * Make new serialized font map data.
+ * Makes new serialized font map data and updates mSerializedFontMap.
*/
- public @Nullable SharedMemory buildNewSerializedFontMap() {
+ public void updateSerializedFontMap() {
try {
final FontConfig fontConfig = getSystemFontConfig();
final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
final Map<String, Typeface> typefaceMap =
SystemFonts.buildSystemTypefaces(fontConfig, fallback);
- return Typeface.serializeFontMap(typefaceMap);
+ SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
+ synchronized (mSerializedFontMapLock) {
+ mSerializedFontMap = serializeFontMap;
+ }
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to serialize updatable font map. "
+ "Retrying with system image fonts.", e);
@@ -278,11 +348,13 @@
final Map<String, Typeface> typefaceMap =
SystemFonts.buildSystemTypefaces(fontConfig, fallback);
- return Typeface.serializeFontMap(typefaceMap);
+ SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
+ synchronized (mSerializedFontMapLock) {
+ mSerializedFontMap = serializeFontMap;
+ }
} catch (IOException | ErrnoException e) {
Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
}
- return null;
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index 5a01a97..2029f39 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -169,8 +169,9 @@
sb.append(c++);
sb.append("]: lang=\"");
sb.append(family.getLocaleList().toLanguageTags());
+ sb.append("\"");
if (family.getVariant() != FontConfig.FontFamily.VARIANT_DEFAULT) {
- sb.append("\", variant=");
+ sb.append(", variant=");
switch (family.getVariant()) {
case FontConfig.FontFamily.VARIANT_COMPACT:
sb.append("Compact");
@@ -304,27 +305,27 @@
String fontPath = shell.getNextArg();
if (fontPath == null) {
throw new SystemFontException(
- FontManager.ERROR_CODE_INVALID_SHELL_ARGUMENT,
+ FontManager.RESULT_ERROR_INVALID_SHELL_ARGUMENT,
"Font file path argument is required.");
}
String signaturePath = shell.getNextArg();
if (signaturePath == null) {
throw new SystemFontException(
- FontManager.ERROR_CODE_INVALID_SHELL_ARGUMENT,
+ FontManager.RESULT_ERROR_INVALID_SHELL_ARGUMENT,
"Signature file argument is required.");
}
ParcelFileDescriptor fontFd = shell.openFileForSystem(fontPath, "r");
if (fontFd == null) {
throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_OPEN_FONT_FILE,
+ FontManager.RESULT_ERROR_FAILED_TO_OPEN_FONT_FILE,
"Failed to open font file");
}
ParcelFileDescriptor sigFd = shell.openFileForSystem(signaturePath, "r");
if (sigFd == null) {
throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_OPEN_SIGNATURE_FILE,
+ FontManager.RESULT_ERROR_FAILED_TO_OPEN_SIGNATURE_FILE,
"Failed to open signature file");
}
@@ -333,24 +334,24 @@
int len = sigFis.available();
if (len > MAX_SIGNATURE_FILE_SIZE_BYTES) {
throw new SystemFontException(
- FontManager.ERROR_CODE_SIGNATURE_TOO_LARGE,
+ FontManager.RESULT_ERROR_SIGNATURE_TOO_LARGE,
"Signature file is too large");
}
byte[] signature = new byte[len];
if (sigFis.read(signature, 0, len) != len) {
throw new SystemFontException(
- FontManager.ERROR_CODE_INVALID_SIGNATURE_FILE,
+ FontManager.RESULT_ERROR_INVALID_SIGNATURE_FILE,
"Invalid read length");
}
- mService.installFontFile(fontFis.getFD(), signature);
+ mService.installFontFile(fontFis.getFD(), signature, -1);
} catch (IOException e) {
throw new SystemFontException(
- FontManager.ERROR_CODE_INVALID_SIGNATURE_FILE,
+ FontManager.RESULT_ERROR_INVALID_SIGNATURE_FILE,
"Failed to read signature file.", e);
}
} catch (IOException e) {
throw new SystemFontException(
- FontManager.ERROR_CODE_INVALID_FONT_FILE,
+ FontManager.RESULT_ERROR_INVALID_FONT_FILE,
"Failed to read font files.", e);
}
@@ -370,7 +371,7 @@
FontConfig config = mService.getSystemFontConfig();
writer.println("Current Version: " + config.getConfigVersion());
- LocalDateTime dt = LocalDateTime.ofEpochSecond(config.getLastModifiedDate(), 0,
+ LocalDateTime dt = LocalDateTime.ofEpochSecond(config.getLastModifiedTimeMillis(), 0,
ZoneOffset.UTC);
writer.println("Last Modified Date: " + dt.format(DateTimeFormatter.ISO_DATE_TIME));
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 720105d..8fac086 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -29,8 +29,6 @@
import android.util.Base64;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
-
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -44,6 +42,11 @@
import java.util.List;
import java.util.Map;
+/**
+ * Manages set of updatable font files.
+ *
+ * <p>This class is not thread safe.
+ */
final class UpdatableFontDir {
private static final String TAG = "UpdatableFontDir";
@@ -113,11 +116,9 @@
private final File mConfigFile;
private final File mTmpConfigFile;
- @GuardedBy("UpdatableFontDir.this")
private final PersistentSystemFontConfig.Config mConfig =
new PersistentSystemFontConfig.Config();
- @GuardedBy("UpdatableFontDir.this")
private int mConfigVersion = 1;
/**
@@ -125,18 +126,11 @@
* FontFileInfo}. All files in this map are validated, and have higher revision numbers than
* corresponding font files in {@link #mPreinstalledFontDirs}.
*/
- @GuardedBy("UpdatableFontDir.this")
private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
FsverityUtil fsverityUtil) {
- mFilesDir = filesDir;
- mPreinstalledFontDirs = preinstalledFontDirs;
- mParser = parser;
- mFsverityUtil = fsverityUtil;
- mConfigFile = new File(CONFIG_XML_FILE);
- mTmpConfigFile = new File(CONFIG_XML_FILE + ".tmp");
- loadFontFileMap();
+ this(filesDir, preinstalledFontDirs, parser, fsverityUtil, new File(CONFIG_XML_FILE));
}
// For unit testing
@@ -148,62 +142,56 @@
mFsverityUtil = fsverityUtil;
mConfigFile = configFile;
mTmpConfigFile = new File(configFile.getAbsoluteFile() + ".tmp");
- loadFontFileMap();
}
- private void loadFontFileMap() {
- // TODO: SIGBUS crash protection
- synchronized (UpdatableFontDir.this) {
- boolean success = false;
+ /* package */ void loadFontFileMap() {
+ boolean success = false;
- try (FileInputStream fis = new FileInputStream(mConfigFile)) {
- PersistentSystemFontConfig.loadFromXml(fis, mConfig);
- } catch (IOException | XmlPullParserException e) {
- mConfig.reset();
+ try (FileInputStream fis = new FileInputStream(mConfigFile)) {
+ PersistentSystemFontConfig.loadFromXml(fis, mConfig);
+ } catch (IOException | XmlPullParserException e) {
+ mConfig.reset();
+ }
+
+ mFontFileInfoMap.clear();
+ try {
+ File[] dirs = mFilesDir.listFiles();
+ if (dirs == null) return;
+ for (File dir : dirs) {
+ if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
+ File[] files = dir.listFiles();
+ if (files == null || files.length != 1) return;
+ FontFileInfo fontFileInfo = validateFontFile(files[0]);
+ addFileToMapIfNewer(fontFileInfo, true /* deleteOldFile */);
}
-
- mFontFileInfoMap.clear();
- try {
- File[] dirs = mFilesDir.listFiles();
- if (dirs == null) return;
- for (File dir : dirs) {
- if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
- File[] files = dir.listFiles();
- if (files == null || files.length != 1) return;
- FontFileInfo fontFileInfo = validateFontFile(files[0]);
- addFileToMapIfNewerLocked(fontFileInfo, true /* deleteOldFile */);
- }
- success = true;
- } catch (Throwable t) {
- // If something happened during loading system fonts, clear all contents in finally
- // block. Here, just dumping errors.
- Slog.e(TAG, "Failed to load font mappings.", t);
- } finally {
- // Delete all files just in case if we find a problematic file.
- if (!success) {
- mFontFileInfoMap.clear();
- FileUtils.deleteContents(mFilesDir);
- }
+ success = true;
+ } catch (Throwable t) {
+ // If something happened during loading system fonts, clear all contents in finally
+ // block. Here, just dumping errors.
+ Slog.e(TAG, "Failed to load font mappings.", t);
+ } finally {
+ // Delete all files just in case if we find a problematic file.
+ if (!success) {
+ mFontFileInfoMap.clear();
+ FileUtils.deleteContents(mFilesDir);
}
}
}
/* package */ void clearUpdates() throws SystemFontException {
- synchronized (UpdatableFontDir.this) {
- mFontFileInfoMap.clear();
- FileUtils.deleteContents(mFilesDir);
+ mFontFileInfoMap.clear();
+ FileUtils.deleteContents(mFilesDir);
- mConfig.reset();
- mConfig.lastModifiedDate = Instant.now().getEpochSecond();
- try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
- PersistentSystemFontConfig.writeToXml(fos, mConfig);
- } catch (Exception e) {
- throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
- "Failed to write config XML.", e);
- }
- mConfigVersion++;
+ mConfig.reset();
+ mConfig.lastModifiedDate = Instant.now().getEpochSecond();
+ try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
+ PersistentSystemFontConfig.writeToXml(fos, mConfig);
+ } catch (Exception e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+ "Failed to write config XML.", e);
}
+ mConfigVersion++;
}
/**
@@ -218,110 +206,108 @@
* @throws SystemFontException if error occurs.
*/
void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws SystemFontException {
- synchronized (UpdatableFontDir.this) {
- File newDir = getRandomDir(mFilesDir);
- if (!newDir.mkdir()) {
+ File newDir = getRandomDir(mFilesDir);
+ if (!newDir.mkdir()) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to create font directory.");
+ }
+ try {
+ // Make newDir executable so that apps can access font file inside newDir.
+ Os.chmod(newDir.getAbsolutePath(), 0711);
+ } catch (ErrnoException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to change mode to 711", e);
+ }
+ boolean success = false;
+ try {
+ File tempNewFontFile = new File(newDir, "font.ttf");
+ try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
+ FileUtils.copy(fd, out.getFD());
+ } catch (IOException e) {
throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
- "Failed to create font directory.");
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to write font file to storage.", e);
}
try {
- // Make newDir executable so that apps can access font file inside newDir.
- Os.chmod(newDir.getAbsolutePath(), 0711);
+ // Do not parse font file before setting up fs-verity.
+ // setUpFsverity throws IOException if failed.
+ mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
+ pkcs7Signature);
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
+ "Failed to setup fs-verity.", e);
+ }
+ String postScriptName;
+ try {
+ postScriptName = mParser.getPostScriptName(tempNewFontFile);
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_INVALID_FONT_FILE,
+ "Failed to read PostScript name from font file", e);
+ }
+ if (postScriptName == null) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_INVALID_FONT_NAME,
+ "Failed to read PostScript name from font file");
+ }
+ File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
+ if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to move verified font file.");
+ }
+ try {
+ // Make the font file readable by apps.
+ Os.chmod(newFontFile.getAbsolutePath(), 0644);
} catch (ErrnoException e) {
throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
"Failed to change mode to 711", e);
}
- boolean success = false;
- try {
- File tempNewFontFile = new File(newDir, "font.ttf");
- try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
- FileUtils.copy(fd, out.getFD());
- } catch (IOException e) {
- throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
- "Failed to write font file to storage.", e);
- }
- try {
- // Do not parse font file before setting up fs-verity.
- // setUpFsverity throws IOException if failed.
- mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
- pkcs7Signature);
- } catch (IOException e) {
- throw new SystemFontException(
- FontManager.ERROR_CODE_VERIFICATION_FAILURE,
- "Failed to setup fs-verity.", e);
- }
- String postScriptName;
- try {
- postScriptName = mParser.getPostScriptName(tempNewFontFile);
- } catch (IOException e) {
- throw new SystemFontException(
- FontManager.ERROR_CODE_INVALID_FONT_FILE,
- "Failed to read PostScript name from font file", e);
- }
- if (postScriptName == null) {
- throw new SystemFontException(
- FontManager.ERROR_CODE_MISSING_POST_SCRIPT_NAME,
- "Failed to read PostScript name from font file");
- }
- File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
- if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
- throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
- "Failed to move verified font file.");
- }
- try {
- // Make the font file readable by apps.
- Os.chmod(newFontFile.getAbsolutePath(), 0644);
- } catch (ErrnoException e) {
- throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
- "Failed to change mode to 711", e);
- }
- FontFileInfo fontFileInfo = validateFontFile(newFontFile);
+ FontFileInfo fontFileInfo = validateFontFile(newFontFile);
- // Write config file.
- PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
- mConfig.copyTo(copied);
+ // Write config file.
+ PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
+ mConfig.copyTo(copied);
- copied.lastModifiedDate = Instant.now().getEpochSecond();
- try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
- PersistentSystemFontConfig.writeToXml(fos, copied);
- } catch (Exception e) {
- throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
- "Failed to write config XML.", e);
- }
+ copied.lastModifiedDate = Instant.now().getEpochSecond();
+ try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
+ PersistentSystemFontConfig.writeToXml(fos, copied);
+ } catch (Exception e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+ "Failed to write config XML.", e);
+ }
- // Backup the mapping for rollback.
- HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
- if (!addFileToMapIfNewerLocked(fontFileInfo, false)) {
- throw new SystemFontException(
- FontManager.ERROR_CODE_DOWNGRADING,
- "Downgrading font file is forbidden.");
- }
+ // Backup the mapping for rollback.
+ HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
+ if (!addFileToMapIfNewer(fontFileInfo, false)) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_DOWNGRADING,
+ "Downgrading font file is forbidden.");
+ }
- if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
- // If we fail to stage the config file, need to rollback the config.
- mFontFileInfoMap.clear();
- mFontFileInfoMap.putAll(backup);
- throw new SystemFontException(
- FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
- "Failed to stage the config file.");
- }
+ if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
+ // If we fail to stage the config file, need to rollback the config.
+ mFontFileInfoMap.clear();
+ mFontFileInfoMap.putAll(backup);
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+ "Failed to stage the config file.");
+ }
- // Now font update is succeeded. Update config version.
- copied.copyTo(mConfig);
- mConfigVersion++;
+ // Now font update is succeeded. Update config version.
+ copied.copyTo(mConfig);
+ mConfigVersion++;
- success = true;
- } finally {
- if (!success) {
- FileUtils.deleteContentsAndDir(newDir);
- }
+ success = true;
+ } finally {
+ if (!success) {
+ FileUtils.deleteContentsAndDir(newDir);
}
}
}
@@ -349,7 +335,7 @@
* higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
* #mPreinstalledFontDirs}).
*/
- private boolean addFileToMapIfNewerLocked(FontFileInfo fontFileInfo, boolean deleteOldFile) {
+ private boolean addFileToMapIfNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) {
String name = fontFileInfo.getFile().getName();
FontFileInfo existingInfo = mFontFileInfoMap.get(name);
final boolean shouldAddToMap;
@@ -400,18 +386,18 @@
private FontFileInfo validateFontFile(File file) throws SystemFontException {
if (!mFsverityUtil.hasFsverity(file.getAbsolutePath())) {
throw new SystemFontException(
- FontManager.ERROR_CODE_VERIFICATION_FAILURE,
+ FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
"Font validation failed. Fs-verity is not enabled: " + file);
}
if (!validateFontFileName(file)) {
throw new SystemFontException(
- FontManager.ERROR_CODE_FONT_NAME_MISMATCH,
+ FontManager.RESULT_ERROR_INVALID_FONT_NAME,
"Font validation failed. Could not validate font file name: " + file);
}
long revision = getFontRevision(file);
if (revision == -1) {
throw new SystemFontException(
- FontManager.ERROR_CODE_INVALID_FONT_FILE,
+ FontManager.RESULT_ERROR_INVALID_FONT_FILE,
"Font validation failed. Could not read font revision: " + file);
}
return new FontFileInfo(file, revision);
@@ -455,21 +441,21 @@
Map<String, File> getFontFileMap() {
Map<String, File> map = new HashMap<>();
- synchronized (UpdatableFontDir.this) {
- for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
- map.put(entry.getKey(), entry.getValue().getFile());
- }
+ for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
+ map.put(entry.getKey(), entry.getValue().getFile());
}
return map;
}
/* package */ FontConfig getSystemFontConfig() {
- synchronized (UpdatableFontDir.this) {
- return SystemFonts.getSystemFontConfig(
- getFontFileMap(),
- mConfig.lastModifiedDate,
- mConfigVersion
- );
- }
+ return SystemFonts.getSystemFontConfig(
+ getFontFileMap(),
+ mConfig.lastModifiedDate,
+ mConfigVersion
+ );
+ }
+
+ /* package */ int getConfigVersion() {
+ return mConfigVersion;
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 23c70ee..2e4200c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -289,6 +289,8 @@
private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
private static native boolean nativeIsVibrating(long ptr, int deviceId);
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 void nativeReloadKeyboardLayouts(long ptr);
private static native void nativeReloadDeviceAliases(long ptr);
private static native String nativeDump(long ptr);
@@ -1818,8 +1820,7 @@
}
private void updateMaximumObscuringOpacityForTouchFromSettings() {
- final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch(
- mContext);
+ final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch();
if (opacity < 0 || opacity > 1) {
Log.e(TAG, "Invalid maximum obscuring opacity " + opacity
+ ", it should be >= 0 and <= 1, rejecting update.");
@@ -2009,6 +2010,18 @@
// Binder call
@Override
+ public int getBatteryStatus(int deviceId) {
+ return nativeGetBatteryStatus(mPtr, deviceId);
+ }
+
+ // Binder call
+ @Override
+ public int getBatteryCapacity(int deviceId) {
+ return nativeGetBatteryCapacity(mPtr, deviceId);
+ }
+
+ // Binder call
+ @Override
public void setPointerIconType(int iconId) {
nativeSetPointerIconType(mPtr, iconId);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6308ace..e5b5350 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4131,48 +4131,52 @@
@BinderThread
@Override
@GuardedBy("mMethodMap")
- public void startProtoDump(byte[] protoDump, int source, String where) {
- if (protoDump == null && source != IME_TRACING_FROM_IMMS) {
- // Dump not triggered from IMMS, but no proto information provided.
- return;
- }
- ImeTracing tracingInstance = ImeTracing.getInstance();
- if (!tracingInstance.isAvailable() || !tracingInstance.isEnabled()) {
- return;
- }
-
- ProtoOutputStream proto = new ProtoOutputStream();
- switch (source) {
- case ImeTracing.IME_TRACING_FROM_CLIENT:
- final long client_token = proto.start(InputMethodClientsTraceFileProto.ENTRY);
- proto.write(InputMethodClientsTraceProto.ELAPSED_REALTIME_NANOS,
- SystemClock.elapsedRealtimeNanos());
- proto.write(InputMethodClientsTraceProto.WHERE, where);
- proto.write(InputMethodClientsTraceProto.CLIENT, protoDump);
- proto.end(client_token);
- break;
- case ImeTracing.IME_TRACING_FROM_IMS:
- final long service_token = proto.start(InputMethodServiceTraceFileProto.ENTRY);
- proto.write(InputMethodServiceTraceProto.ELAPSED_REALTIME_NANOS,
- SystemClock.elapsedRealtimeNanos());
- proto.write(InputMethodServiceTraceProto.WHERE, where);
- proto.write(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE, protoDump);
- proto.end(service_token);
- break;
- case IME_TRACING_FROM_IMMS:
- final long managerservice_token =
- proto.start(InputMethodManagerServiceTraceFileProto.ENTRY);
- proto.write(InputMethodManagerServiceTraceProto.ELAPSED_REALTIME_NANOS,
- SystemClock.elapsedRealtimeNanos());
- proto.write(InputMethodManagerServiceTraceProto.WHERE, where);
- dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE);
- proto.end(managerservice_token);
- break;
- default:
- // Dump triggered by a source not recognised.
+ public void startProtoDump(byte[] protoDump, int source, String where,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> {
+ if (protoDump == null && source != IME_TRACING_FROM_IMMS) {
+ // Dump not triggered from IMMS, but no proto information provided.
return;
- }
- tracingInstance.addToBuffer(proto, source);
+ }
+ ImeTracing tracingInstance = ImeTracing.getInstance();
+ if (!tracingInstance.isAvailable() || !tracingInstance.isEnabled()) {
+ return;
+ }
+
+ ProtoOutputStream proto = new ProtoOutputStream();
+ switch (source) {
+ case ImeTracing.IME_TRACING_FROM_CLIENT:
+ final long client_token = proto.start(InputMethodClientsTraceFileProto.ENTRY);
+ proto.write(InputMethodClientsTraceProto.ELAPSED_REALTIME_NANOS,
+ SystemClock.elapsedRealtimeNanos());
+ proto.write(InputMethodClientsTraceProto.WHERE, where);
+ proto.write(InputMethodClientsTraceProto.CLIENT, protoDump);
+ proto.end(client_token);
+ break;
+ case ImeTracing.IME_TRACING_FROM_IMS:
+ final long service_token = proto.start(InputMethodServiceTraceFileProto.ENTRY);
+ proto.write(InputMethodServiceTraceProto.ELAPSED_REALTIME_NANOS,
+ SystemClock.elapsedRealtimeNanos());
+ proto.write(InputMethodServiceTraceProto.WHERE, where);
+ proto.write(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE, protoDump);
+ proto.end(service_token);
+ break;
+ case IME_TRACING_FROM_IMMS:
+ final long managerservice_token =
+ proto.start(InputMethodManagerServiceTraceFileProto.ENTRY);
+ proto.write(InputMethodManagerServiceTraceProto.ELAPSED_REALTIME_NANOS,
+ SystemClock.elapsedRealtimeNanos());
+ proto.write(InputMethodManagerServiceTraceProto.WHERE, where);
+ dumpDebug(proto,
+ InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE);
+ proto.end(managerservice_token);
+ break;
+ default:
+ // Dump triggered by a source not recognised.
+ return;
+ }
+ tracingInstance.addToBuffer(proto, source);
+ });
}
@BinderThread
@@ -4183,40 +4187,44 @@
@BinderThread
@Override
- public void startImeTrace() {
- ImeTracing.getInstance().startTrace(null /* printwriter */);
- ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
- clients = new ArrayMap<>(mClients);
- }
- for (ClientState state : clients.values()) {
- if (state != null) {
- try {
- state.client.setImeTraceEnabled(true /* enabled */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error while trying to enable ime trace on client window", e);
+ public void startImeTrace(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> {
+ ImeTracing.getInstance().startTrace(null /* printwriter */);
+ ArrayMap<IBinder, ClientState> clients;
+ synchronized (mMethodMap) {
+ clients = new ArrayMap<>(mClients);
+ }
+ for (ClientState state : clients.values()) {
+ if (state != null) {
+ try {
+ state.client.setImeTraceEnabled(true /* enabled */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error while trying to enable ime trace on client window", e);
+ }
}
}
- }
+ });
}
@BinderThread
@Override
- public void stopImeTrace() {
- ImeTracing.getInstance().stopTrace(null /* printwriter */);
- ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
- clients = new ArrayMap<>(mClients);
- }
- for (ClientState state : clients.values()) {
- if (state != null) {
- try {
- state.client.setImeTraceEnabled(false /* enabled */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error while trying to disable ime trace on client window", e);
+ public void stopImeTrace(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> {
+ ImeTracing.getInstance().stopTrace(null /* printwriter */);
+ ArrayMap<IBinder, ClientState> clients;
+ synchronized (mMethodMap) {
+ clients = new ArrayMap<>(mClients);
+ }
+ for (ClientState state : clients.values()) {
+ if (state != null) {
+ try {
+ state.client.setImeTraceEnabled(false /* enabled */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error while trying to disable ime trace on client window", e);
+ }
}
}
- }
+ });
}
@GuardedBy("mMethodMap")
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 7f9c766..6fec906 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1868,7 +1868,9 @@
@BinderThread
@Override
- public void startProtoDump(byte[] clientProtoDump, int source, String where) {
+ public void startProtoDump(byte[] clientProtoDump, int source, String where,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> { });
}
@BinderThread
@@ -1879,12 +1881,14 @@
@BinderThread
@Override
- public void startImeTrace() {
+ public void startImeTrace(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> { });
}
@BinderThread
@Override
- public void stopImeTrace() {
+ public void stopImeTrace(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> { });
}
}
}
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 dc1a26a..785e674 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -16,6 +16,7 @@
package com.android.server.location.contexthub;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -320,6 +321,10 @@
@Override
public void onNanoAppDisabled(long nanoAppId) {
}
+
+ @Override
+ public void onClientAuthorizationChanged(long nanoAppId, int authorization) {
+ }
};
}
@@ -697,6 +702,7 @@
*
* @param contextHubId the ID of the hub this client is attached to
* @param clientCallback the client interface to register with the service
+ * @param attributionTag an optional attribution tag within the given package
* @return the generated client interface, null if registration was unsuccessful
* @throws IllegalArgumentException if contextHubId is not a valid ID
* @throws IllegalStateException if max number of clients have already registered
@@ -704,7 +710,8 @@
*/
@Override
public IContextHubClient createClient(
- int contextHubId, IContextHubClientCallback clientCallback) throws RemoteException {
+ int contextHubId, IContextHubClientCallback clientCallback,
+ @Nullable String attributionTag) throws RemoteException {
checkPermissions();
if (!isValidContextHubId(contextHubId)) {
throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
@@ -723,13 +730,15 @@
* @param contextHubId the ID of the hub this client is attached to
* @param pendingIntent the PendingIntent associated with this client
* @param nanoAppId the ID of the nanoapp PendingIntent events will be sent for
+ * @param attributionTag an optional attribution tag within the given package
* @return the generated client interface
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
* @throws IllegalStateException if there were too many registered clients at the service
*/
@Override
public IContextHubClient createPendingIntentClient(
- int contextHubId, PendingIntent pendingIntent, long nanoAppId) throws RemoteException {
+ int contextHubId, PendingIntent pendingIntent, long nanoAppId,
+ @Nullable String attributionTag) throws RemoteException {
checkPermissions();
if (!isValidContextHubId(contextHubId)) {
throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
diff --git a/services/core/java/com/android/server/location/contexthub/OWNERS b/services/core/java/com/android/server/location/contexthub/OWNERS
index d4393d6..90c2330 100644
--- a/services/core/java/com/android/server/location/contexthub/OWNERS
+++ b/services/core/java/com/android/server/location/contexthub/OWNERS
@@ -1,2 +1,3 @@
arthuri@google.com
bduddie@google.com
+stange@google.com
diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
index 8399f54..a1e18bd 100644
--- a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
+++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
@@ -106,6 +106,8 @@
private final Context mContext;
private final ComponentName mComponentName;
private IResumeOnRebootService mBinder;
+ @Nullable
+ ServiceConnection mServiceConnection;
private ResumeOnRebootServiceConnection(Context context,
@NonNull ComponentName componentName) {
@@ -115,17 +117,9 @@
/** Unbind from the service */
public void unbindService() {
- mContext.unbindService(new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mBinder = null;
-
- }
- });
+ if (mServiceConnection != null) {
+ mContext.unbindService(mServiceConnection);
+ }
}
/** Bind to the service */
@@ -134,17 +128,19 @@
CountDownLatch connectionLatch = new CountDownLatch(1);
Intent intent = new Intent();
intent.setComponent(mComponentName);
- final boolean success = mContext.bindServiceAsUser(intent, new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mBinder = IResumeOnRebootService.Stub.asInterface(service);
- connectionLatch.countDown();
- }
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mBinder = IResumeOnRebootService.Stub.asInterface(service);
+ connectionLatch.countDown();
+ }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- },
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBinder = null;
+ }
+ };
+ final boolean success = mContext.bindServiceAsUser(intent, mServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
BackgroundThread.getHandler(), UserHandle.SYSTEM);
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index ea2788c..6d1c680 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -155,7 +155,7 @@
try {
// Use the privileged method because Lockdown VPN is initiated by the system, so
// no additional permission checks are necessary.
- mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, egressProp);
+ mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, null, egressProp);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
Log.e(TAG, "Failed to start VPN", e);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 29eaf4f..ca21640 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -243,6 +243,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -1253,7 +1254,7 @@
// identified carrier, which may want to manage their own notifications. This method
// should be called every time the carrier config changes anyways, and there's no
// reason to alert if there isn't a carrier.
- return;
+ continue;
}
final boolean notifyWarning = getBooleanDefeatingNullable(config,
@@ -2163,13 +2164,14 @@
if (template.matches(probeIdent)) {
if (LOGD) {
Slog.d(TAG, "Found template " + template + " which matches subscriber "
- + NetworkIdentity.scrubSubscriberId(subscriberId));
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
}
return false;
}
}
- Slog.i(TAG, "No policy for subscriber " + NetworkIdentity.scrubSubscriberId(subscriberId)
+ Slog.i(TAG, "No policy for subscriber "
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId)
+ "; generating default policy");
final NetworkPolicy policy = buildDefaultMobilePolicy(subId, subscriberId);
addNetworkPolicyAL(policy);
@@ -3614,14 +3616,15 @@
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
- fout.println(subId + "=" + NetworkIdentity.scrubSubscriberId(subscriberId));
+ fout.println(subId + "="
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
}
fout.decreaseIndent();
fout.println();
for (String[] mergedSubscribers : mMergedSubscriberIds) {
fout.println("Merged subscriptions: " + Arrays.toString(
- NetworkIdentity.scrubSubscriberId(mergedSubscribers)));
+ NetworkIdentityUtils.scrubSubscriberIds(mergedSubscribers)));
}
fout.println();
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 4faa790..d042b88 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -27,7 +27,7 @@
import android.annotation.Nullable;
import android.net.INetd;
import android.net.NetworkStats;
-import android.net.VpnInfo;
+import android.net.UnderlyingNetworkInfo;
import android.net.util.NetdService;
import android.os.RemoteException;
import android.os.StrictMode;
@@ -81,7 +81,7 @@
private final Object mPersistentDataLock = new Object();
/** Set containing info about active VPNs and their underlying networks. */
- private volatile VpnInfo[] mVpnInfos = new VpnInfo[0];
+ private volatile UnderlyingNetworkInfo[] mUnderlyingNetworkInfos = new UnderlyingNetworkInfo[0];
// A persistent snapshot of cumulative stats since device start
@GuardedBy("mPersistentDataLock")
@@ -116,8 +116,8 @@
*
* @param vpnArray The snapshot of the currently-running VPNs.
*/
- public void updateVpnInfos(VpnInfo[] vpnArray) {
- mVpnInfos = vpnArray.clone();
+ public void updateUnderlyingNetworkInfos(UnderlyingNetworkInfo[] vpnArray) {
+ mUnderlyingNetworkInfos = vpnArray.clone();
}
/**
@@ -319,7 +319,7 @@
// code that will acquire other locks within the system server. See b/134244752.
synchronized (mPersistentDataLock) {
// Take a reference. If this gets swapped out, we still have the old reference.
- final VpnInfo[] vpnArray = mVpnInfos;
+ final UnderlyingNetworkInfo[] vpnArray = mUnderlyingNetworkInfos;
// Take a defensive copy. mPersistSnapshot is mutated in some cases below
final NetworkStats prev = mPersistSnapshot.clone();
@@ -369,8 +369,8 @@
}
@GuardedBy("mPersistentDataLock")
- private NetworkStats adjustForTunAnd464Xlat(
- NetworkStats uidDetailStats, NetworkStats previousStats, VpnInfo[] vpnArray) {
+ private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats,
+ NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray) {
// Calculate delta from last snapshot
final NetworkStats delta = uidDetailStats.subtract(previousStats);
@@ -381,8 +381,9 @@
delta.apply464xlatAdjustments(mStackedIfaces);
// Migrate data usage over a VPN to the TUN network.
- for (VpnInfo info : vpnArray) {
- delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
+ for (UnderlyingNetworkInfo info : vpnArray) {
+ delta.migrateTun(info.ownerUid, info.iface,
+ info.underlyingIfaces.toArray(new String[0]));
// Filter out debug entries as that may lead to over counting.
delta.filterDebugEntries();
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 4be7b48..0ab35a9 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -104,8 +104,8 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
+import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
-import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
@@ -973,7 +973,7 @@
Network[] defaultNetworks,
NetworkState[] networkStates,
String activeIface,
- VpnInfo[] vpnInfos) {
+ UnderlyingNetworkInfo[] underlyingNetworkInfos) {
checkNetworkStackPermission(mContext);
final long token = Binder.clearCallingIdentity();
@@ -986,7 +986,7 @@
// Update the VPN underlying interfaces only after the poll is made and tun data has been
// migrated. Otherwise the migration would use the new interfaces instead of the ones that
// were current when the polled data was transferred.
- mStatsFactory.updateVpnInfos(vpnInfos);
+ mStatsFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e32c00f..6843733 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -377,7 +377,8 @@
static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] {
Adjustment.KEY_CONTEXTUAL_ACTIONS,
- Adjustment.KEY_TEXT_REPLIES};
+ Adjustment.KEY_TEXT_REPLIES,
+ Adjustment.KEY_NOT_CONVERSATION};
static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
RoleManager.ROLE_DIALER,
@@ -2313,6 +2314,13 @@
} else if ("false".equals(value)) {
mAssistants.disallowAdjustmentType(Adjustment.KEY_RANKING_SCORE);
}
+ } else if (SystemUiDeviceConfigFlags.ENABLE_NAS_NOT_CONVERSATION.equals(name)) {
+ String value = properties.getString(name, null);
+ if ("true".equals(value)) {
+ mAssistants.allowAdjustmentType(Adjustment.KEY_NOT_CONVERSATION);
+ } else if ("false".equals(value)) {
+ mAssistants.disallowAdjustmentType(Adjustment.KEY_NOT_CONVERSATION);
+ }
}
}
};
@@ -9302,21 +9310,30 @@
Slog.v(TAG, "onNotificationEnqueuedLocked() called with: r = [" + r + "]");
}
final StatusBarNotification sbn = r.getSbn();
- notifyAssistantLocked(
- sbn,
- r.getNotificationType(),
- true /* sameUserOnly */,
- (assistant, sbnHolder) -> {
- try {
- if (debug) {
- Slog.v(TAG,
- "calling onNotificationEnqueuedWithChannel " + sbnHolder);
- }
- assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel());
- } catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ boolean sbnVisible = isVisibleToListener(
+ sbn, r.getNotificationType(), info)
+ && info.isSameUser(r.getUserId());
+ if (sbnVisible) {
+ TrimCache trimCache = new TrimCache(sbn);
+ final INotificationListener assistant = (INotificationListener) info.service;
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+ final StatusBarNotificationHolder sbnHolder =
+ new StatusBarNotificationHolder(sbnToPost);
+ try {
+ if (debug) {
+ Slog.v(TAG,
+ "calling onNotificationEnqueuedWithChannel " + sbnHolder);
}
- });
+ final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
+ assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel(),
+ update);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+ }
+ }
+ }
}
@GuardedBy("mNotificationLock")
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 61c8b17..461d519 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1419,8 +1419,10 @@
conversation.setPkg(p.pkg);
conversation.setUid(p.uid);
conversation.setNotificationChannel(nc);
- conversation.setParentChannelLabel(
- p.channels.get(nc.getParentChannelId()).getName());
+ NotificationChannel parent = p.channels.get(nc.getParentChannelId());
+ conversation.setParentChannelLabel(parent == null
+ ? null
+ : parent.getName());
boolean blockedByGroup = false;
if (nc.getGroup() != null) {
NotificationChannelGroup group = p.groups.get(nc.getGroup());
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index f59934f..d7bc3bb 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -523,27 +523,39 @@
if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
long timeStartMs = System.currentTimeMillis();
for (final String handle: mPendingLookups) {
+ final String cacheKey = getCacheKey(mContext.getUserId(), handle);
LookupResult lookupResult = null;
- final Uri uri = Uri.parse(handle);
- if ("tel".equals(uri.getScheme())) {
- if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
- lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
- } else if ("mailto".equals(uri.getScheme())) {
- if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
- lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
- } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
- if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
- lookupResult = searchContacts(mContext, uri);
- } else {
- lookupResult = new LookupResult(); // invalid person for the cache
- if (!"name".equals(uri.getScheme())) {
- Slog.w(TAG, "unsupported URI " + handle);
+ boolean cacheHit = false;
+ synchronized (mPeopleCache) {
+ lookupResult = mPeopleCache.get(cacheKey);
+ if (lookupResult != null && !lookupResult.isExpired()) {
+ // The name wasn't already added to the cache, no need to retry
+ cacheHit = true;
+ }
+ }
+ if (!cacheHit) {
+ final Uri uri = Uri.parse(handle);
+ if ("tel".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
+ lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
+ } else if ("mailto".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
+ lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
+ } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+ if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
+ lookupResult = searchContacts(mContext, uri);
+ } else {
+ lookupResult = new LookupResult(); // invalid person for the cache
+ if (!"name".equals(uri.getScheme())) {
+ Slog.w(TAG, "unsupported URI " + handle);
+ }
}
}
if (lookupResult != null) {
- synchronized (mPeopleCache) {
- final String cacheKey = getCacheKey(mContext.getUserId(), handle);
- mPeopleCache.put(cacheKey, lookupResult);
+ if (!cacheHit) {
+ synchronized (mPeopleCache) {
+ mPeopleCache.put(cacheKey, lookupResult);
+ }
}
if (DEBUG) {
Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
@@ -580,4 +592,3 @@
}
}
}
-
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index f8990c0..5d7c41c 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -588,29 +588,32 @@
*
* @param recipientUid the uid gaining visibility of the {@code visibleUid}.
* @param visibleUid the uid becoming visible to the {@recipientUid}
+ * @return {@code true} if implicit access was not already granted.
*/
- public void grantImplicitAccess(int recipientUid, int visibleUid) {
- if (recipientUid != visibleUid) {
- final boolean changed = mImplicitlyQueryable.add(recipientUid, visibleUid);
- if (changed && DEBUG_LOGGING) {
- Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
- }
- synchronized (mCacheLock) {
- if (mShouldFilterCache != null) {
- // update the cache in a one-off manner since we've got all the information we
- // need.
- SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid);
- if (visibleUids == null) {
- visibleUids = new SparseBooleanArray();
- mShouldFilterCache.put(recipientUid, visibleUids);
- }
- visibleUids.put(visibleUid, false);
+ public boolean grantImplicitAccess(int recipientUid, int visibleUid) {
+ if (recipientUid == visibleUid) {
+ return false;
+ }
+ final boolean changed = mImplicitlyQueryable.add(recipientUid, visibleUid);
+ if (changed && DEBUG_LOGGING) {
+ Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
+ }
+ synchronized (mCacheLock) {
+ if (mShouldFilterCache != null) {
+ // update the cache in a one-off manner since we've got all the information we
+ // need.
+ SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid);
+ if (visibleUids == null) {
+ visibleUids = new SparseBooleanArray();
+ mShouldFilterCache.put(recipientUid, visibleUids);
}
- }
- if (changed) {
- onChanged();
+ visibleUids.put(visibleUid, false);
}
}
+ if (changed) {
+ onChanged();
+ }
+ return changed;
}
public void onSystemReady() {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index e0b57e4..402f646 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -118,8 +118,8 @@
// Schedule a one-off job which scans installed packages and updates
// out-of-date oat files.
js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
- .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
- .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
+ .setMinimumLatency(TimeUnit.MINUTES.toMillis(10))
+ .setOverrideDeadline(TimeUnit.MINUTES.toMillis(60))
.build());
// Schedule a daily job which scans installed packages and compiles
diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
index da65fe2..c65c2b1 100644
--- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
+++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
@@ -234,7 +234,7 @@
List<EventLog.Event> events = new ArrayList<>();
EventLog.readEvents(tags, events);
-
+ Matcher matcher = EXECUTE_NATIVE_AUDIT_PATTERN.matcher("");
for (int i = 0; i < events.size(); ++i) {
if (mAuditWatchingStopRequested) {
Log.w(TAG, "Stopping AuditWatchingJob run at scheduler request");
@@ -259,7 +259,9 @@
// And then use a regular expression to verify it's one of the messages we're
// interested in and to extract the path of the file being loaded.
- Matcher matcher = EXECUTE_NATIVE_AUDIT_PATTERN.matcher(message);
+ // Reuse the Matcher to avoid unnecessary string garbage caused by libcore's
+ // regex matching.
+ matcher.reset(message);
if (!matcher.matches()) {
continue;
}
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index c3bca28..15e1d52 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -470,23 +470,34 @@
return instantGrantList.get(instantAppId);
}
+ /**
+ * Allows an app to see an instant app.
+ *
+ * @param userId the userId in which this access is being granted
+ * @param intent when provided, this serves as the intent that caused
+ * this access to be granted
+ * @param recipientUid the uid of the app receiving visibility
+ * @param instantAppId the app ID of the instant app being made visible
+ * to the recipient
+ * @return {@code true} if access is granted.
+ */
@GuardedBy("mService.mLock")
- public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
+ public boolean grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
int recipientUid, int instantAppId) {
if (mInstalledInstantAppUids == null) {
- return; // no instant apps installed; no need to grant
+ return false; // no instant apps installed; no need to grant
}
WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
if (instantAppList == null || !instantAppList.get(instantAppId)) {
- return; // instant app id isn't installed; no need to grant
+ return false; // instant app id isn't installed; no need to grant
}
if (instantAppList.get(recipientUid)) {
- return; // target app id is an instant app; no need to grant
+ return false; // target app id is an instant app; no need to grant
}
if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
final Set<String> categories = intent.getCategories();
if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
- return; // launched via VIEW/BROWSABLE intent; no need to grant
+ return false; // launched via VIEW/BROWSABLE intent; no need to grant
}
}
WatchedSparseArray<WatchedSparseBooleanArray> targetAppList = mInstantGrants.get(userId);
@@ -500,6 +511,7 @@
targetAppList.put(recipientUid, instantGrantList);
}
instantGrantList.put(instantAppId, true /*granted*/);
+ return true;
}
@GuardedBy("mService.mLock")
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 13fe8a0..6485c0c 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -407,8 +408,14 @@
boolean needsRenderScriptOverride = false;
if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
&& NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
- abiList = Build.SUPPORTED_32_BIT_ABIS;
- needsRenderScriptOverride = true;
+ if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ needsRenderScriptOverride = true;
+ } else {
+ throw new PackageManagerException(
+ INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
+ "Apks with renderscript are not supported on 64-bit only devices");
+ }
}
final int copyRet;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4eaac2e4..7c42569 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3299,17 +3299,28 @@
}
}
+ private void linkFile(String relativePath, String fromBase, String toBase) throws IOException {
+ try {
+ // Try
+ if (mIncrementalFileStorages != null && mIncrementalFileStorages.makeLink(relativePath,
+ fromBase, toBase)) {
+ return;
+ }
+ mPm.mInstaller.linkFile(relativePath, fromBase, toBase);
+ } catch (InstallerException | IOException e) {
+ throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
+ + fromBase + ", " + toBase + ")", e);
+ }
+ }
+
private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
throws IOException {
for (File fromFile : fromFiles) {
final String relativePath = getRelativePath(fromFile, fromDir);
- try {
- mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
- toDir.getAbsolutePath());
- } catch (InstallerException e) {
- throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
- + fromDir + ", " + toDir + ")", e);
- }
+ final String fromBase = fromDir.getAbsolutePath();
+ final String toBase = toDir.getAbsolutePath();
+
+ linkFile(relativePath, fromBase, toBase);
}
Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
@@ -3577,12 +3588,6 @@
// Retrying commit.
if (mIncrementalFileStorages != null) {
- try {
- mIncrementalFileStorages.startLoading();
- } catch (IOException e) {
- throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
- e.getCause());
- }
return false;
}
@@ -3757,9 +3762,15 @@
};
try {
+ final PackageInfo pkgInfo = mPm.getPackageInfo(this.params.appPackageName, 0,
+ userId);
+ final File inheritedDir =
+ (pkgInfo != null && pkgInfo.applicationInfo != null) ? new File(
+ pkgInfo.applicationInfo.getCodePath()).getParentFile() : null;
+
mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
- params, statusListener, healthCheckParams, healthListener, addedFiles,
- perUidReadTimeouts);
+ inheritedDir, params, statusListener, healthCheckParams, healthListener,
+ addedFiles, perUidReadTimeouts);
return false;
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d2fc5b4..6e5bd94 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -347,6 +347,7 @@
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.AttributeCache;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
@@ -355,7 +356,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.permission.persistence.RuntimePermissionsPersistence;
-import com.android.server.AttributeCache;
import com.android.server.DeviceIdleInternal;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
@@ -3863,7 +3863,8 @@
int i = 0;
for (int index = 0; index < N; index++) {
final PackageSetting ps = sus.packages.valueAt(index);
- if (ps.getInstalled(userId)) {
+ if (ps.getInstalled(userId)
+ && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
res[i++] = ps.name;
}
}
@@ -18163,13 +18164,15 @@
return false;
}
- String codePath = codeFile.getAbsolutePath();
- if (mIncrementalManager != null && isIncrementalPath(codePath)) {
- mIncrementalManager.onPackageRemoved(codePath);
- }
+ final boolean isIncremental = (mIncrementalManager != null && isIncrementalPath(
+ codeFile.getAbsolutePath()));
removeCodePathLI(codeFile);
+ if (isIncremental) {
+ mIncrementalManager.onPackageRemoved(codeFile);
+ }
+
return true;
}
@@ -18817,9 +18820,11 @@
final VersionInfo versionInfo = request.versionInfos.get(installPackageName);
final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
+ final boolean isRollback = installArgs != null
+ && installArgs.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
final boolean compatMatch = verifySignatures(signatureCheckPs,
disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
- compareRecover);
+ compareRecover, isRollback);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
removeAppKeySetData = true;
@@ -19789,6 +19794,7 @@
final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
final boolean virtualPreload =
((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+ final boolean isRollback = args.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
@ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.move != null) {
// moving a complete application; perform an initial scan on the new install location
@@ -19969,7 +19975,8 @@
parsedPackage);
// We don't care about disabledPkgSetting on install for now.
final boolean compatMatch = verifySignatures(signatureCheckPs, null,
- parsedPackage.getSigningDetails(), compareCompat, compareRecover);
+ parsedPackage.getSigningDetails(), compareCompat, compareRecover,
+ isRollback);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
synchronized (mLock) {
@@ -20246,15 +20253,23 @@
+ pkgName11);
}
} else {
+ SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails();
+ SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails();
// default to original signature matching
- if (!parsedPackage.getSigningDetails().checkCapability(
- oldPackage.getSigningDetails(),
+ if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails,
SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !oldPackage.getSigningDetails().checkCapability(
- parsedPackage.getSigningDetails(),
+ && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails,
SigningDetails.CertCapabilities.ROLLBACK)) {
- throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package has a different signature: " + pkgName11);
+ // Allow the update to proceed if this is a rollback and the parsed
+ // package's current signing key is the current signer or in the lineage
+ // of the old package; this allows a rollback to a previously installed
+ // version after an app's signing key has been rotated without requiring
+ // the rollback capability on the previous signing key.
+ if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf(
+ parsedPkgSigningDetails)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package has a different signature: " + pkgName11);
+ }
}
}
@@ -27173,6 +27188,7 @@
final boolean instantApp =
isInstantAppInternal(visiblePackage.getPackageName(), userId, visibleUid);
+ final boolean accessGranted;
if (instantApp) {
if (!direct) {
// if the interaction that lead to this granting access to an instant app
@@ -27180,10 +27196,13 @@
// grant.
return;
}
- mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
+ accessGranted = mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
} else {
- mAppsFilter.grantImplicitAccess(recipientUid, visibleUid);
+ accessGranted = mAppsFilter.grantImplicitAccess(recipientUid, visibleUid);
+ }
+ if (accessGranted) {
+ ApplicationPackageManager.invalidateGetPackagesForUidCache();
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index ee94b85..8015063 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -624,7 +624,7 @@
*/
public static boolean verifySignatures(PackageSetting pkgSetting,
PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
- boolean compareCompat, boolean compareRecover)
+ boolean compareCompat, boolean compareRecover, boolean isRollback)
throws PackageManagerException {
final String packageName = pkgSetting.name;
boolean compatMatch = false;
@@ -658,6 +658,13 @@
match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
}
+ if (!match && isRollback) {
+ // Since a rollback can only be initiated for an APK previously installed on the
+ // device allow rolling back to a previous signing key even if the rollback
+ // capability has not been granted.
+ match = pkgSetting.signatures.mSigningDetails.hasAncestorOrSelf(parsedSignatures);
+ }
+
if (!match) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Package " + packageName +
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 3207d56a..212edf6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -122,6 +122,7 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
@@ -155,6 +156,8 @@
boolean mComponents;
int mQueryFlags;
+ private static final SecureRandom RANDOM = new SecureRandom();
+
PackageManagerShellCommand(PackageManagerService service, Context context) {
mInterface = service;
mLegacyPermissionManager = LocalServices.getService(LegacyPermissionManagerInternal.class);
@@ -3146,7 +3149,7 @@
// 1. Single file from stdin.
if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
- final String name = "base." + (isApex ? "apex" : "apk");
+ final String name = "base" + RANDOM.nextInt() + "." + (isApex ? "apex" : "apk");
final Metadata metadata = Metadata.forStdIn(name);
session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes,
metadata.toByteArray(), null);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 2929568..349d556 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4881,7 +4881,7 @@
}
if (ps.sharedUser == null || permissionNames != null || dumpAll) {
- dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState);
+ dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState, users);
}
if (dumpAllComponents) {
@@ -5131,9 +5131,12 @@
continue;
}
- dumpInstallPermissionsLPr(pw, prefix, permissionNames, permissionsState);
+ List<UserInfo> users = getAllUsers(UserManagerService.getInstance());
- for (int userId : UserManagerService.getInstance().getUserIds()) {
+ dumpInstallPermissionsLPr(pw, prefix, permissionNames, permissionsState, users);
+
+ for (UserInfo user : users) {
+ final int userId = user.id;
final int[] gids = mPermissionDataProvider.getGidsForUid(UserHandle.getUid(
userId, su.userId));
final Collection<PermissionState> permissions =
@@ -5247,31 +5250,55 @@
}
}
- void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
- LegacyPermissionState permissionsState) {
- Collection<PermissionState> permissionStates = permissionsState.getPermissionStates(
- UserHandle.USER_SYSTEM);
- boolean hasInstallPermissions = false;
- for (PermissionState permissionState : permissionStates) {
- if (!permissionState.isRuntime()) {
- hasInstallPermissions = true;
- break;
- }
- }
- if (hasInstallPermissions) {
- pw.print(prefix); pw.println("install permissions:");
+ void dumpInstallPermissionsLPr(PrintWriter pw, String prefix,
+ ArraySet<String> filterPermissionNames, LegacyPermissionState permissionsState,
+ List<UserInfo> users) {
+ ArraySet<String> dumpPermissionNames = new ArraySet<>();
+ for (UserInfo user : users) {
+ int userId = user.id;
+ Collection<PermissionState> permissionStates = permissionsState.getPermissionStates(
+ userId);
for (PermissionState permissionState : permissionStates) {
if (permissionState.isRuntime()) {
continue;
}
- if (permissionNames != null
- && !permissionNames.contains(permissionState.getName())) {
+ String permissionName = permissionState.getName();
+ if (filterPermissionNames != null
+ && !filterPermissionNames.contains(permissionName)) {
continue;
}
- pw.print(prefix); pw.print(" "); pw.print(permissionState.getName());
- pw.print(": granted="); pw.print(permissionState.isGranted());
- pw.println(permissionFlagsToString(", flags=",
- permissionState.getFlags()));
+ dumpPermissionNames.add(permissionName);
+ }
+ }
+ boolean printedSomething = false;
+ for (String permissionName : dumpPermissionNames) {
+ PermissionState systemPermissionState = permissionsState.getPermissionState(
+ permissionName, UserHandle.USER_SYSTEM);
+ for (UserInfo user : users) {
+ int userId = user.id;
+ PermissionState permissionState;
+ if (userId == UserHandle.USER_SYSTEM) {
+ permissionState = systemPermissionState;
+ } else {
+ permissionState = permissionsState.getPermissionState(permissionName, userId);
+ if (Objects.equals(permissionState, systemPermissionState)) {
+ continue;
+ }
+ }
+ if (!printedSomething) {
+ pw.print(prefix); pw.println("install permissions:");
+ printedSomething = true;
+ }
+ pw.print(prefix); pw.print(" "); pw.print(permissionName);
+ pw.print(": granted="); pw.print(
+ permissionState != null && permissionState.isGranted());
+ pw.print(permissionFlagsToString(", flags=",
+ permissionState != null ? permissionState.getFlags() : 0));
+ if (userId == UserHandle.USER_SYSTEM) {
+ pw.println();
+ } else {
+ pw.print(", userId="); pw.println(userId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f43240b..a807777 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3880,6 +3880,7 @@
*/
@Override
public boolean removeUser(@UserIdInt int userId) {
+ Slog.i(LOG_TAG, "removeUser u" + userId);
checkManageOrCreateUsersPermission("Only the system can remove users");
final String restriction = getUserRemovalRestriction(userId);
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index 851ddd1..e8be9b6 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -19,6 +19,7 @@
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
@@ -33,6 +34,7 @@
import android.os.Build;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.permission.PermissionManager;
import android.util.DisplayMetrics;
import android.util.Slog;
@@ -42,6 +44,7 @@
import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.File;
+import java.util.List;
/**
* The v2 of {@link PackageParser} for use when parsing is initiated in the server and must
@@ -118,10 +121,15 @@
displayMetrics.setToDefaults();
}
+ PermissionManager permissionManager = ActivityThread.currentApplication()
+ .getSystemService(PermissionManager.class);
+ List<PermissionManager.SplitPermissionInfo> splitPermissions = permissionManager
+ .getSplitPermissions();
+
mCacher = cacheDir == null ? null : new PackageCacher(cacheDir);
parsingUtils = new ParsingPackageUtils(onlyCoreApps, separateProcesses, displayMetrics,
- callback);
+ splitPermissions, callback);
ParseInput.Callback enforcementCallback = (changeId, packageName, targetSdkVersion) -> {
ApplicationInfo appInfo = mSharedAppInfo.get();
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
index 92f22a4..e8eae47 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
@@ -104,6 +104,26 @@
}
/**
+ * Get the permission state for a permission and a user.
+ *
+ * @param permissionName the permission name
+ * @param userId the user ID
+ * @return the permission state
+ *
+ * @hide
+ */
+ @Nullable
+ public PermissionState getPermissionState(@NonNull String permissionName,
+ @UserIdInt int userId) {
+ checkUserId(userId);
+ UserState userState = mUserStates.get(userId);
+ if (userState == null) {
+ return null;
+ }
+ return userState.getPermissionState(permissionName);
+ }
+
+ /**
* Put a permission state for a user.
*
* @param permissionState the permission state
@@ -142,10 +162,10 @@
}
/**
- * Get all the runtime permission states for a user.
+ * Get all the permission states for a user.
*
* @param userId the user ID
- * @return the runtime permission states
+ * @return the permission states
*/
@NonNull
public Collection<PermissionState> getPermissionStates(@UserIdInt int userId) {
@@ -301,5 +321,25 @@
public int getFlags() {
return mFlags;
}
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+ PermissionState that = (PermissionState) object;
+ return mRuntime == that.mRuntime
+ && mGranted == that.mGranted
+ && mFlags == that.mFlags
+ && Objects.equals(mName, that.mName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mRuntime, mGranted, mFlags);
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index e901f66f..ac358db 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -34,10 +34,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
import com.android.server.policy.devicestate.config.Conditions;
-import com.android.server.policy.devicestate.config.DeviceState;
import com.android.server.policy.devicestate.config.DeviceStateConfig;
import com.android.server.policy.devicestate.config.LidSwitchCondition;
import com.android.server.policy.devicestate.config.NumericRange;
@@ -54,6 +55,7 @@
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.BooleanSupplier;
@@ -82,7 +84,7 @@
private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
@VisibleForTesting
- static final int DEFAULT_DEVICE_STATE = 0;
+ static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
@@ -116,31 +118,38 @@
@VisibleForTesting
static DeviceStateProviderImpl createFromConfig(@NonNull Context context,
@Nullable ReadableConfig readableConfig) {
- SparseArray<Conditions> conditionsForState = new SparseArray<>();
+ List<DeviceState> deviceStateList = new ArrayList<>();
+ List<Conditions> conditionsList = new ArrayList<>();
+
if (readableConfig != null) {
DeviceStateConfig config = parseConfig(readableConfig);
if (config != null) {
- for (DeviceState stateConfig : config.getDeviceState()) {
- int state = stateConfig.getIdentifier().intValue();
- Conditions conditions = stateConfig.getConditions();
- conditionsForState.put(state, conditions);
+ for (com.android.server.policy.devicestate.config.DeviceState stateConfig :
+ config.getDeviceState()) {
+ final int state = stateConfig.getIdentifier().intValue();
+ final String name = stateConfig.getName() == null ? "" : stateConfig.getName();
+ deviceStateList.add(new DeviceState(state, name));
+
+ final Conditions condition = stateConfig.getConditions();
+ conditionsList.add(condition);
}
}
}
- if (conditionsForState.size() == 0) {
- conditionsForState.put(DEFAULT_DEVICE_STATE, null);
+ if (deviceStateList.size() == 0) {
+ deviceStateList.add(DEFAULT_DEVICE_STATE);
+ conditionsList.add(null);
}
- return new DeviceStateProviderImpl(context, conditionsForState);
+ return new DeviceStateProviderImpl(context, deviceStateList, conditionsList);
}
// Lock for internal state.
private final Object mLock = new Object();
private final Context mContext;
- // List of supported states in ascending order.
- private final int[] mOrderedStates;
- // Map of state to a boolean supplier that returns true when all required conditions are met for
- // the device to be in the state.
+ // List of supported states in ascending order based on their identifier.
+ private final DeviceState[] mOrderedStates;
+ // Map of state identifier to a boolean supplier that returns true when all required conditions
+ // are met for the device to be in the state.
private final SparseArray<BooleanSupplier> mStateConditions;
@Nullable
@@ -155,12 +164,16 @@
private final Map<Sensor, SensorEvent> mLatestSensorEvent = new ArrayMap<>();
private DeviceStateProviderImpl(@NonNull Context context,
- @NonNull SparseArray<Conditions> conditionsForState) {
+ @NonNull List<DeviceState> deviceStates,
+ @NonNull List<Conditions> stateConditions) {
+ Preconditions.checkArgument(deviceStates.size() == stateConditions.size(),
+ "Number of device states must be equal to the number of device state conditions.");
+
mContext = context;
- mOrderedStates = new int[conditionsForState.size()];
- for (int i = 0; i < conditionsForState.size(); i++) {
- mOrderedStates[i] = conditionsForState.keyAt(i);
- }
+
+ DeviceState[] orderedStates = deviceStates.toArray(new DeviceState[deviceStates.size()]);
+ Arrays.sort(orderedStates, Comparator.comparingInt(DeviceState::getIdentifier));
+ mOrderedStates = orderedStates;
// Whether or not this instance should register to receive lid switch notifications from
// InputManagerInternal. If there are no device state conditions that are based on the lid
@@ -171,9 +184,9 @@
final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>();
mStateConditions = new SparseArray<>();
- for (int i = 0; i < mOrderedStates.length; i++) {
- int state = mOrderedStates[i];
- Conditions conditions = conditionsForState.get(state);
+ for (int i = 0; i < stateConditions.size(); i++) {
+ final int state = deviceStates.get(i).getIdentifier();
+ final Conditions conditions = stateConditions.get(i);
if (conditions == null) {
mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
continue;
@@ -261,7 +274,7 @@
/** Notifies the listener that the set of supported device states has changed. */
private void notifySupportedStatesChanged() {
- int[] supportedStates;
+ DeviceState[] supportedStates;
synchronized (mLock) {
if (mListener == null) {
return;
@@ -281,9 +294,9 @@
return;
}
- int newState = mOrderedStates[0];
+ int newState = mOrderedStates[0].getIdentifier();
for (int i = 0; i < mOrderedStates.length; i++) {
- int state = mOrderedStates[i];
+ int state = mOrderedStates[i].getIdentifier();
if (mStateConditions.get(state).getAsBoolean()) {
newState = state;
break;
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index ff51237..c10e828 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -209,16 +209,23 @@
* resource.
*/
private class DeviceStateListener implements DeviceStateManager.DeviceStateListener {
- private final int mFoldedDeviceState;
+ private final int[] mFoldedDeviceStates;
DeviceStateListener(Context context) {
- mFoldedDeviceState = context.getResources().getInteger(
- com.android.internal.R.integer.config_foldedDeviceState);
+ mFoldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
}
@Override
public void onDeviceStateChanged(int deviceState) {
- setDeviceFolded(deviceState == mFoldedDeviceState);
+ boolean folded = false;
+ for (int i = 0; i < mFoldedDeviceStates.length; i++) {
+ if (deviceState == mFoldedDeviceStates[i]) {
+ folded = true;
+ break;
+ }
+ }
+ setDeviceFolded(folded);
}
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 89e7986..a407e8e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -188,7 +188,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutofillManagerInternal;
@@ -201,7 +200,9 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.internal.policy.KeyInterceptionInfo;
+import com.android.internal.policy.LogDecelerateInterpolator;
import com.android.internal.policy.PhoneWindow;
+import com.android.internal.policy.TransitionAnimation;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
import com.android.server.ExtconStateObserver;
@@ -228,7 +229,6 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
-import java.util.List;
/**
* WindowManagerPolicy implementation for the Android phone UI. This
@@ -582,7 +582,7 @@
private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions =
new SparseArray<KeyCharacterMap.FallbackAction>();
- private final LogDecelerateInterpolator mLogDecelerateInterpolator
+ private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator
= new LogDecelerateInterpolator(100, 0);
private final MutableBoolean mTmpBoolean = new MutableBoolean(false);
@@ -2484,28 +2484,8 @@
@Override
public Animation createHiddenByKeyguardExit(boolean onWallpaper,
boolean goingToNotificationShade, boolean subtleAnimation) {
- if (goingToNotificationShade) {
- return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_behind_enter_fade_in);
- }
-
- final int resource;
- if (subtleAnimation) {
- resource = R.anim.lock_screen_behind_enter_subtle;
- } else if (onWallpaper) {
- resource = R.anim.lock_screen_behind_enter_wallpaper;
- } else {
- resource = R.anim.lock_screen_behind_enter;
- }
-
- AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(mContext, resource);
-
- // TODO: Use XML interpolators when we have log interpolators available in XML.
- final List<Animation> animations = set.getAnimations();
- for (int i = animations.size() - 1; i >= 0; --i) {
- animations.get(i).setInterpolator(mLogDecelerateInterpolator);
- }
-
- return set;
+ return TransitionAnimation.createHiddenByKeyguardExit(mContext,
+ mLogDecelerateInterpolator, onWallpaper, goingToNotificationShade, subtleAnimation);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index db4b6d0..8e0d632 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2892,8 +2892,8 @@
PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
- mDisplayReady = mDisplayManagerInternal.requestPowerState(displayPowerRequest,
- mRequestWaitForNegativeProximity);
+ mDisplayReady = mDisplayManagerInternal.requestPowerState(Display.DEFAULT_DISPLAY_GROUP,
+ displayPowerRequest, mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
if (DEBUG_SPEW) {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 5fe5db6..be5c2e1 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -21,7 +21,9 @@
import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -245,10 +247,50 @@
future, energyConsumerIds));
return future;
}
+
+ @Override
+ public PowerEntity[] getPowerEntityInfo() {
+ return getPowerStatsHal().getPowerEntityInfo();
+ }
+
+ @Override
+ public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+ int[] powerEntityIds) {
+ final CompletableFuture<StateResidencyResult[]> future = new CompletableFuture<>();
+ mHandler.sendMessage(
+ PooledLambda.obtainMessage(PowerStatsService.this::getStateResidencyAsync,
+ future, powerEntityIds));
+ return future;
+ }
+
+ @Override
+ public Channel[] getEnergyMeterInfo() {
+ return getPowerStatsHal().getEnergyMeterInfo();
+ }
+
+ @Override
+ public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+ int[] channelIds) {
+ final CompletableFuture<EnergyMeasurement[]> future = new CompletableFuture<>();
+ mHandler.sendMessage(
+ PooledLambda.obtainMessage(PowerStatsService.this::readEnergyMeterAsync,
+ future, channelIds));
+ return future;
+ }
}
private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future,
int[] energyConsumerIds) {
future.complete(getPowerStatsHal().getEnergyConsumed(energyConsumerIds));
}
+
+ private void getStateResidencyAsync(CompletableFuture<StateResidencyResult[]> future,
+ int[] powerEntityIds) {
+ future.complete(getPowerStatsHal().getStateResidency(powerEntityIds));
+ }
+
+ private void readEnergyMeterAsync(CompletableFuture<EnergyMeasurement[]> future,
+ int[] channelIds) {
+ future.complete(getPowerStatsHal().readEnergyMeters(channelIds));
+ }
}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index e12991a..9560f59 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -563,6 +563,7 @@
params.setRequestDowngrade(true);
params.setRequiredInstalledVersionCode(
pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
+ params.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
if (isStaged()) {
params.setStaged();
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 8a1c778..3dbc32a 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -159,7 +159,7 @@
final Intent intent = new Intent(
RotationResolverService.SERVICE_INTERFACE).setPackage(resolvedPackage);
- final ResolveInfo resolveInfo = context.getPackageManager().resolveActivityAsUser(intent,
+ final ResolveInfo resolveInfo = context.getPackageManager().resolveServiceAsUser(intent,
flags, context.getUserId());
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
Slog.wtf(TAG, String.format("Service %s not found in package %s",
diff --git a/services/core/java/com/android/server/timedetector/DeviceConfig.java b/services/core/java/com/android/server/timedetector/DeviceConfig.java
new file mode 100644
index 0000000..7b9ad0f
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/DeviceConfig.java
@@ -0,0 +1,126 @@
+/*
+ * 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.timedetector;
+
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+
+import java.time.Duration;
+import java.util.concurrent.Executor;
+
+/**
+ * A helper class for reading / monitoring the {@link
+ * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace for server-configured flags.
+ */
+public final class DeviceConfig {
+
+ /**
+ * An annotation used to indicate when a {@link
+ * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} key is required.
+ *
+ * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider
+ * also shares the {@link android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the
+ * prefix "geotz_" on all of its key strings.
+ */
+ @StringDef(prefix = "KEY_", value = {
+ KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED,
+ KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT,
+ KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+ })
+ @interface DeviceConfigKey {}
+
+ /**
+ * The key to force location time zone detection on for a device. Only intended for use during
+ * release testing with droidfooders. The user can still disable the feature by turning off the
+ * master location switch, or disabling automatic time zone detection.
+ */
+ @DeviceConfigKey
+ public static final String KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED =
+ "force_location_time_zone_detection_enabled";
+
+ /**
+ * The key for the default value used to determine whether location time zone detection is
+ * enabled when the user hasn't explicitly set it yet.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT =
+ "location_time_zone_detection_enabled_default";
+
+ /**
+ * The key for the minimum delay after location time zone detection has been enabled before the
+ * location time zone manager can report it is uncertain about the time zone.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
+ "location_time_zone_detection_uncertainty_delay_millis";
+
+ /**
+ * The key for the timeout passed to a location time zone provider that tells it how long it has
+ * to provide an explicit first suggestion without being declared uncertain.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
+ "ltpz_init_timeout_millis";
+
+ /**
+ * The key for the extra time added to {@link
+ * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
+ * manager before the location time zone provider will actually be declared uncertain.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
+ "ltpz_init_timeout_fuzz_millis";
+
+ /** Creates an instance. */
+ public DeviceConfig() {}
+
+ /** Adds a listener for the system_time namespace. */
+ public void addListener(
+ @NonNull Executor handlerExecutor, @NonNull Runnable listener) {
+ android.provider.DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_SYSTEM_TIME,
+ handlerExecutor,
+ properties -> listener.run());
+ }
+
+ /**
+ * Returns a boolean value from {@link android.provider.DeviceConfig} from the system_time
+ * namespace, or {@code defaultValue} if there is no explicit value set.
+ */
+ public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) {
+ return android.provider.DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
+ }
+
+ /**
+ * Returns a positive duration from {@link android.provider.DeviceConfig} from the system_time
+ * namespace, or {@code defaultValue} if there is no explicit value set.
+ */
+ @Nullable
+ public Duration getDurationFromMillis(
+ @DeviceConfigKey String key, @Nullable Duration defaultValue) {
+ long deviceConfigValue =
+ android.provider.DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1);
+ if (deviceConfigValue < 0) {
+ return defaultValue;
+ }
+ return Duration.ofMillis(deviceConfigValue);
+ }
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
similarity index 91%
rename from services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
rename to services/core/java/com/android/server/timedetector/EnvironmentImpl.java
index 4f3f9dc..5cd1718 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
+++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
@@ -25,7 +25,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.os.Build;
-import android.os.Environment;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -39,11 +38,11 @@
import java.util.Objects;
/**
- * The real implementation of {@link TimeDetectorStrategyImpl.Callback} used on device.
+ * The real implementation of {@link TimeDetectorStrategyImpl.Environment} used on device.
*/
-public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategyImpl.Callback {
+public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment {
- private final static String TAG = "timedetector.TimeDetectorStrategyCallbackImpl";
+ private static final String TAG = TimeDetectorService.TAG;
private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
@@ -52,7 +51,7 @@
* incorrect for sure.
*/
private static final Instant TIME_LOWER_BOUND = Instant.ofEpochMilli(
- Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
+ Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME));
/**
* By default telephony and network only suggestions are accepted and telephony takes
@@ -74,7 +73,7 @@
@NonNull private final AlarmManager mAlarmManager;
@NonNull private final int[] mOriginPriorities;
- public TimeDetectorStrategyCallbackImpl(@NonNull Context context) {
+ public EnvironmentImpl(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
mContentResolver = Objects.requireNonNull(context.getContentResolver());
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 71b1a49..bbbd19f 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -50,7 +50,7 @@
* implementation to deal with the logic around time detection.
*/
public final class TimeDetectorService extends ITimeDetectorService.Stub {
- private static final String TAG = "TimeDetectorService";
+ static final String TAG = "time_detector";
public static class Lifecycle extends SystemService {
@@ -73,8 +73,8 @@
@NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
private static TimeDetectorService create(@NonNull Context context) {
- TimeDetectorStrategyImpl.Callback callback = new TimeDetectorStrategyCallbackImpl(context);
- TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(callback);
+ TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context);
+ TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(environment);
Handler handler = FgThread.getHandler();
TimeDetectorService timeDetectorService =
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 2fbeb75..7cd4184 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -40,6 +40,7 @@
import java.time.Instant;
import java.util.Arrays;
+import java.util.Objects;
/**
* An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to
@@ -51,7 +52,7 @@
public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
private static final boolean DBG = false;
- private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
+ private static final String LOG_TAG = TimeDetectorService.TAG;
/** A score value used to indicate "no score", either due to validation failure or age. */
private static final int TELEPHONY_INVALID_SCORE = -1;
@@ -88,7 +89,7 @@
private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
@NonNull
- private final Callback mCallback;
+ private final Environment mEnvironment;
// Used to store the last time the system clock state was set automatically. It is used to
// detect (and log) issues with the realtime clock or whether the clock is being set without
@@ -127,7 +128,7 @@
* moved to {@link TimeDetectorStrategy}. There are similar issues with
* {@link #systemClockMillis()} while any process can modify the system clock.
*/
- public interface Callback {
+ public interface Environment {
/**
* The absolute threshold below which the system clock need not be updated. i.e. if setting
@@ -170,8 +171,8 @@
void releaseWakeLock();
}
- TimeDetectorStrategyImpl(@NonNull Callback callback) {
- mCallback = callback;
+ TimeDetectorStrategyImpl(@NonNull Environment environment) {
+ mEnvironment = Objects.requireNonNull(environment);
}
@Override
@@ -267,7 +268,7 @@
@Override
public synchronized void handleAutoTimeConfigChanged() {
- boolean enabled = mCallback.isAutoTimeDetectionEnabled();
+ boolean enabled = mEnvironment.isAutoTimeDetectionEnabled();
// When automatic time detection is enabled we update the system clock instantly if we can.
// Conversely, when automatic time detection is disabled we leave the clock as it is.
if (enabled) {
@@ -286,20 +287,20 @@
ipw.increaseIndent(); // level 1
ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
- ipw.println("mCallback.isAutoTimeDetectionEnabled()="
- + mCallback.isAutoTimeDetectionEnabled());
- ipw.println("mCallback.elapsedRealtimeMillis()=" + mCallback.elapsedRealtimeMillis());
- ipw.println("mCallback.systemClockMillis()=" + mCallback.systemClockMillis());
- ipw.println("mCallback.systemClockUpdateThresholdMillis()="
- + mCallback.systemClockUpdateThresholdMillis());
- ipw.printf("mCallback.autoTimeLowerBound()=%s(%s)\n",
- mCallback.autoTimeLowerBound(),
- mCallback.autoTimeLowerBound().toEpochMilli());
+ ipw.println("mEnvironment.isAutoTimeDetectionEnabled()="
+ + mEnvironment.isAutoTimeDetectionEnabled());
+ ipw.println("mEnvironment.elapsedRealtimeMillis()=" + mEnvironment.elapsedRealtimeMillis());
+ ipw.println("mEnvironment.systemClockMillis()=" + mEnvironment.systemClockMillis());
+ ipw.println("mEnvironment.systemClockUpdateThresholdMillis()="
+ + mEnvironment.systemClockUpdateThresholdMillis());
+ ipw.printf("mEnvironment.autoTimeLowerBound()=%s(%s)\n",
+ mEnvironment.autoTimeLowerBound(),
+ mEnvironment.autoTimeLowerBound().toEpochMilli());
String priorities =
- Arrays.stream(mCallback.autoOriginPriorities())
+ Arrays.stream(mEnvironment.autoOriginPriorities())
.mapToObj(TimeDetectorStrategy::originToString)
.collect(joining(",", "[", "]"));
- ipw.println("mCallback.autoOriginPriorities()=" + priorities);
+ ipw.println("mEnvironment.autoOriginPriorities()=" + priorities);
ipw.println("Time change log:");
ipw.increaseIndent(); // level 2
@@ -372,7 +373,7 @@
}
// We can validate the suggestion against the reference time clock.
- long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
if (elapsedRealtimeMillis < newUtcTime.getReferenceTimeMillis()) {
// elapsedRealtime clock went backwards?
Slog.w(LOG_TAG, "New reference time is in the future? Ignoring."
@@ -391,7 +392,7 @@
private boolean validateSuggestionAgainstLowerBound(
@NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) {
- Instant lowerBound = mCallback.autoTimeLowerBound();
+ Instant lowerBound = mEnvironment.autoTimeLowerBound();
// Suggestion is definitely wrong if it comes before lower time bound.
if (lowerBound.isAfter(Instant.ofEpochMilli(newUtcTime.getValue()))) {
@@ -405,13 +406,13 @@
@GuardedBy("this")
private void doAutoTimeDetection(@NonNull String detectionReason) {
- if (!mCallback.isAutoTimeDetectionEnabled()) {
+ if (!mEnvironment.isAutoTimeDetectionEnabled()) {
// Avoid doing unnecessary work with this (race-prone) check.
return;
}
// Try the different origins one at a time.
- int[] originPriorities = mCallback.autoOriginPriorities();
+ int[] originPriorities = mEnvironment.autoOriginPriorities();
for (int origin : originPriorities) {
TimestampedValue<Long> newUtcTime = null;
String cause = null;
@@ -470,7 +471,7 @@
@GuardedBy("this")
@Nullable
private TelephonyTimeSuggestion findBestTelephonySuggestion() {
- long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
// Telephony time suggestions are assumed to be derived from NITZ or NITZ-like signals.
// These have a number of limitations:
@@ -579,7 +580,7 @@
}
TimestampedValue<Long> utcTime = networkSuggestion.getUtcTime();
- long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
@@ -599,7 +600,7 @@
}
TimestampedValue<Long> utcTime = gnssTimeSuggestion.getUtcTime();
- long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
@@ -619,7 +620,7 @@
}
TimestampedValue<Long> utcTime = externalTimeSuggestion.getUtcTime();
- long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
@@ -634,7 +635,7 @@
boolean isOriginAutomatic = isOriginAutomatic(origin);
if (isOriginAutomatic) {
- if (!mCallback.isAutoTimeDetectionEnabled()) {
+ if (!mEnvironment.isAutoTimeDetectionEnabled()) {
if (DBG) {
Slog.d(LOG_TAG, "Auto time detection is not enabled."
+ " origin=" + originToString(origin)
@@ -644,7 +645,7 @@
return false;
}
} else {
- if (mCallback.isAutoTimeDetectionEnabled()) {
+ if (mEnvironment.isAutoTimeDetectionEnabled()) {
if (DBG) {
Slog.d(LOG_TAG, "Auto time detection is enabled."
+ " origin=" + originToString(origin)
@@ -655,11 +656,11 @@
}
}
- mCallback.acquireWakeLock();
+ mEnvironment.acquireWakeLock();
try {
return setSystemClockUnderWakeLock(origin, time, cause);
} finally {
- mCallback.releaseWakeLock();
+ mEnvironment.releaseWakeLock();
}
}
@@ -671,9 +672,9 @@
private boolean setSystemClockUnderWakeLock(
@Origin int origin, @NonNull TimestampedValue<Long> newTime, @NonNull String cause) {
- long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
boolean isOriginAutomatic = isOriginAutomatic(origin);
- long actualSystemClockMillis = mCallback.systemClockMillis();
+ long actualSystemClockMillis = mEnvironment.systemClockMillis();
if (isOriginAutomatic) {
// CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
// may be setting the clock.
@@ -701,7 +702,7 @@
// Check if the new signal would make sufficient difference to the system clock. If it's
// below the threshold then ignore it.
long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
- long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
+ long systemClockUpdateThreshold = mEnvironment.systemClockUpdateThresholdMillis();
if (absTimeDifference < systemClockUpdateThreshold) {
if (DBG) {
Slog.d(LOG_TAG, "Not setting system clock. New time and"
@@ -715,7 +716,7 @@
return true;
}
- mCallback.setSystemClock(newSystemClockMillis);
+ mEnvironment.setSystemClock(newSystemClockMillis);
String logMsg = "Set system clock using time=" + newTime
+ " cause=" + cause
+ " elapsedRealtimeMillis=" + elapsedRealtimeMillis
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
similarity index 78%
rename from services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
rename to services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 1357608..f52b9b1 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -33,43 +33,52 @@
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
+import com.android.server.timedetector.DeviceConfig;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
- * The real implementation of {@link TimeZoneDetectorStrategyImpl.Callback}.
+ * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
*/
-public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategyImpl.Callback {
+public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment {
- private static final String LOG_TAG = "TimeZoneDetectorCallbackImpl";
+ private static final String LOG_TAG = TimeZoneDetectorService.TAG;
private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
@NonNull private final ContentResolver mCr;
@NonNull private final UserManager mUserManager;
- @NonNull private final boolean mGeoDetectionFeatureEnabled;
+ @NonNull private final DeviceConfig mDeviceConfig;
+ @NonNull private final boolean mGeoDetectionSupported;
@NonNull private final LocationManager mLocationManager;
+
// @NonNull after setConfigChangeListener() is called.
+ @GuardedBy("this")
private ConfigurationChangeListener mConfigChangeListener;
- TimeZoneDetectorCallbackImpl(@NonNull Context context, @NonNull Handler handler,
- boolean geoDetectionFeatureEnabled) {
+ EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
+ @NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) {
mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler);
+ Executor handlerExecutor = new HandlerExecutor(mHandler);
mCr = context.getContentResolver();
mUserManager = context.getSystemService(UserManager.class);
mLocationManager = context.getSystemService(LocationManager.class);
- mGeoDetectionFeatureEnabled = geoDetectionFeatureEnabled;
+ mDeviceConfig = deviceConfig;
+ mGeoDetectionSupported = geoDetectionSupported;
- // Wire up the change listener. All invocations are performed on the mHandler thread.
+ // Wire up the change listeners. All invocations are performed on the mHandler thread.
// Listen for the user changing / the user's location mode changing.
IntentFilter filter = new IntentFilter();
@@ -103,18 +112,29 @@
handleConfigChangeOnHandlerThread();
}
}, UserHandle.USER_ALL);
+
+ // Add async callbacks for changes to server-side flags: some of the flags affect device /
+ // user config. All changes can be treated like a config change. If flags that affect config
+ // haven't changed then call will be a no-op.
+ mDeviceConfig.addListener(
+ handlerExecutor,
+ this::handleConfigChangeOnHandlerThread);
}
private void handleConfigChangeOnHandlerThread() {
- if (mConfigChangeListener == null) {
- Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
+ synchronized (this) {
+ if (mConfigChangeListener == null) {
+ Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
+ }
+ mConfigChangeListener.onChange();
}
- mConfigChangeListener.onChange();
}
@Override
public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
- mConfigChangeListener = Objects.requireNonNull(listener);
+ synchronized (this) {
+ mConfigChangeListener = Objects.requireNonNull(listener);
+ }
}
@Override
@@ -174,7 +194,10 @@
// time zone detection: if we wrote it down then we'd set the value explicitly, which
// would prevent detecting "default" later. That might influence what happens on later
// releases that support geo detection on the same hardware.
- if (isGeoDetectionSupported()) {
+ // Also avoid writing the geo detection enabled setting for devices that are currently
+ // force-enabled: otherwise we might overwrite a droidfood user's real setting
+ // permanently.
+ if (isGeoDetectionSupported() && !isGeoDetectionForceEnabled()) {
final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
}
@@ -191,7 +214,7 @@
}
private boolean isGeoDetectionSupported() {
- return mGeoDetectionFeatureEnabled;
+ return mGeoDetectionSupported;
}
private boolean isAutoDetectionEnabled() {
@@ -213,12 +236,25 @@
}
private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
- final boolean geoDetectionEnabledByDefault = false;
+ // We may never use this, but it gives us a way to force location-based time zone detection
+ // on for testers (where their other settings allow).
+ boolean forceEnabled = isGeoDetectionForceEnabled();
+ if (forceEnabled) {
+ return true;
+ }
+
+ final boolean geoDetectionEnabledByDefault = mDeviceConfig.getBoolean(
+ DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, false);
return Settings.Secure.getIntForUser(mCr,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
(geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
}
+ private boolean isGeoDetectionForceEnabled() {
+ return mDeviceConfig.getBoolean(
+ DeviceConfig.KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, false);
+ }
+
private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) {
// See comment in setAutoDetectionEnabledIfRequired. http://b/171953500
if (isGeoDetectionEnabled(userId) != enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
index b63df05..4eb1b99 100644
--- a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
+++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
@@ -19,8 +19,11 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
+import java.time.Duration;
import java.util.ArrayDeque;
/**
@@ -49,18 +52,15 @@
*/
public final class ReferenceWithHistory<V> {
- private static final Object NULL_MARKER = "{null marker}";
-
/** The maximum number of references to store. */
private final int mMaxHistorySize;
- /**
- * The history storage. Note that ArrayDeque doesn't support {@code null} so this stores Object
- * and not V. Use {@link #packNullIfRequired(Object)} and {@link #unpackNullIfRequired(Object)}
- * to convert to / from the storage object.
- */
+ /** The number of times {@link #set(Object)} has been called. */
+ private int mSetCount;
+
+ /** The history storage. */
@Nullable
- private ArrayDeque<Object> mValues;
+ private ArrayDeque<TimestampedValue<V>> mValues;
/**
* Creates an instance that records, at most, the specified number of values.
@@ -78,8 +78,8 @@
if (mValues == null || mValues.isEmpty()) {
return null;
}
- Object value = mValues.getFirst();
- return unpackNullIfRequired(value);
+ TimestampedValue<V> valueHolder = mValues.getFirst();
+ return valueHolder.getValue();
}
/**
@@ -98,8 +98,10 @@
V previous = get();
- Object nullSafeValue = packNullIfRequired(newValue);
- mValues.addFirst(nullSafeValue);
+ TimestampedValue<V> valueHolder =
+ new TimestampedValue<>(SystemClock.elapsedRealtime(), newValue);
+ mValues.addFirst(valueHolder);
+ mSetCount++;
return previous;
}
@@ -110,10 +112,13 @@
if (mValues == null) {
ipw.println("{Empty}");
} else {
- int i = 0;
- for (Object value : mValues) {
- ipw.println(i + ": " + unpackNullIfRequired(value));
- i++;
+ int i = mSetCount;
+ for (TimestampedValue<V> valueHolder : mValues) {
+ ipw.print(--i);
+ ipw.print("@");
+ ipw.print(Duration.ofMillis(valueHolder.getReferenceTimeMillis()).toString());
+ ipw.print(": ");
+ ipw.println(valueHolder.getValue());
}
}
ipw.flush();
@@ -130,23 +135,4 @@
public String toString() {
return String.valueOf(get());
}
-
- /**
- * Turns a non-nullable Object into a nullable value. See also
- * {@link #packNullIfRequired(Object)}.
- */
- @SuppressWarnings("unchecked")
- @Nullable
- private V unpackNullIfRequired(@NonNull Object value) {
- return value == NULL_MARKER ? null : (V) value;
- }
-
- /**
- * Turns a nullable value into a non-nullable Object. See also
- * {@link #unpackNullIfRequired(Object)}.
- */
- @NonNull
- private Object packNullIfRequired(@Nullable V value) {
- return value == null ? NULL_MARKER : value;
- }
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 2ead3be..bd71ddf 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -59,7 +59,7 @@
public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
implements IBinder.DeathRecipient {
- private static final String TAG = "TimeZoneDetectorService";
+ static final String TAG = "time_zone_detector";
/**
* A "feature switch" for location-based time zone detection. If this is {@code false}. It is
@@ -67,19 +67,19 @@
* is important.
*/
@Nullable
- private static Boolean sGeoLocationTimeZoneDetectionEnabled;
+ private static Boolean sGeoLocationTimeZoneDetectionSupported;
/** Returns {@code true} if the location-based time zone detection feature is enabled. */
- public static boolean isGeoLocationTimeZoneDetectionEnabled(Context context) {
- if (sGeoLocationTimeZoneDetectionEnabled == null) {
+ public static boolean isGeoLocationTimeZoneDetectionSupported(Context context) {
+ if (sGeoLocationTimeZoneDetectionSupported == null) {
// The config value is expected to be the main switch. Platform developers can also
// enable the feature using a persistent system property.
- sGeoLocationTimeZoneDetectionEnabled = context.getResources().getBoolean(
+ sGeoLocationTimeZoneDetectionSupported = context.getResources().getBoolean(
com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
|| SystemProperties.getBoolean(
"persist.sys.location_time_zone_detection_feature_enabled", false);
}
- return sGeoLocationTimeZoneDetectionEnabled;
+ return sGeoLocationTimeZoneDetectionSupported;
}
/**
@@ -98,11 +98,11 @@
Context context = getContext();
Handler handler = FgThread.getHandler();
- boolean geolocationTimeZoneDetectionEnabled =
- isGeoLocationTimeZoneDetectionEnabled(context);
+ boolean geolocationTimeZoneDetectionSupported =
+ isGeoLocationTimeZoneDetectionSupported(context);
TimeZoneDetectorStrategy timeZoneDetectorStrategy =
TimeZoneDetectorStrategyImpl.create(
- context, handler, geolocationTimeZoneDetectionEnabled);
+ context, handler, geolocationTimeZoneDetectionSupported);
// Create and publish the local service for use by internal callers.
TimeZoneDetectorInternal internal =
@@ -330,7 +330,7 @@
boolean isGeoTimeZoneDetectionSupported() {
enforceManageTimeZoneDetectorPermission();
- return isGeoLocationTimeZoneDetectionEnabled(mContext);
+ return isGeoLocationTimeZoneDetectionSupported(mContext);
}
@Override
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 781668b..c464b74 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -38,6 +38,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.timedetector.DeviceConfig;
import java.util.ArrayList;
import java.util.List;
@@ -59,7 +60,7 @@
* conditions.
*/
@VisibleForTesting
- public interface Callback {
+ public interface Environment {
/**
* Sets a {@link ConfigurationChangeListener} that will be invoked when there are any
@@ -97,7 +98,7 @@
void storeConfiguration(@UserIdInt int userId, TimeZoneConfiguration newConfiguration);
}
- private static final String LOG_TAG = "TimeZoneDetectorStrategy";
+ private static final String LOG_TAG = TimeZoneDetectorService.TAG;
private static final boolean DBG = false;
/**
@@ -164,7 +165,7 @@
private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10;
@NonNull
- private final Callback mCallback;
+ private final Environment mEnvironment;
@GuardedBy("this")
@NonNull
@@ -203,17 +204,18 @@
*/
public static TimeZoneDetectorStrategyImpl create(
@NonNull Context context, @NonNull Handler handler,
- boolean geolocationTimeZoneDetectionEnabled) {
+ boolean geoDetectionSupported) {
- TimeZoneDetectorCallbackImpl callback = new TimeZoneDetectorCallbackImpl(
- context, handler, geolocationTimeZoneDetectionEnabled);
- return new TimeZoneDetectorStrategyImpl(callback);
+ DeviceConfig deviceConfig = new DeviceConfig();
+ EnvironmentImpl environment = new EnvironmentImpl(
+ context, handler, deviceConfig, geoDetectionSupported);
+ return new TimeZoneDetectorStrategyImpl(environment);
}
@VisibleForTesting
- public TimeZoneDetectorStrategyImpl(@NonNull Callback callback) {
- mCallback = Objects.requireNonNull(callback);
- mCallback.setConfigChangeListener(this::handleConfigChanged);
+ public TimeZoneDetectorStrategyImpl(@NonNull Environment environment) {
+ mEnvironment = Objects.requireNonNull(environment);
+ mEnvironment.setConfigChangeListener(this::handleConfigChanged);
}
/**
@@ -230,13 +232,13 @@
@Override
@NonNull
public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
- return mCallback.getConfigurationInternal(userId);
+ return mEnvironment.getConfigurationInternal(userId);
}
@Override
@NonNull
public synchronized ConfigurationInternal getCurrentUserConfigurationInternal() {
- int currentUserId = mCallback.getCurrentUserId();
+ int currentUserId = mEnvironment.getCurrentUserId();
return getConfigurationInternal(currentUserId);
}
@@ -257,9 +259,9 @@
return false;
}
- // Store the configuration / notify as needed. This will cause the mCallback to invoke
+ // Store the configuration / notify as needed. This will cause the mEnvironment to invoke
// handleConfigChanged() asynchronously.
- mCallback.storeConfiguration(userId, newConfiguration);
+ mEnvironment.storeConfiguration(userId, newConfiguration);
String logMsg = "Configuration changed:"
+ " oldConfiguration=" + oldConfiguration
@@ -275,8 +277,9 @@
public synchronized void suggestGeolocationTimeZone(
@NonNull GeolocationTimeZoneSuggestion suggestion) {
- int currentUserId = mCallback.getCurrentUserId();
- ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ConfigurationInternal currentUserConfig =
+ mEnvironment.getConfigurationInternal(currentUserId);
if (DBG) {
Slog.d(LOG_TAG, "Geolocation suggestion received."
+ " currentUserConfig=" + currentUserConfig
@@ -299,7 +302,7 @@
public synchronized boolean suggestManualTimeZone(
@UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion) {
- int currentUserId = mCallback.getCurrentUserId();
+ int currentUserId = mEnvironment.getCurrentUserId();
if (userId != currentUserId) {
Slog.w(LOG_TAG, "Manual suggestion received but user != current user, userId=" + userId
+ " suggestion=" + suggestion);
@@ -332,8 +335,9 @@
public synchronized void suggestTelephonyTimeZone(
@NonNull TelephonyTimeZoneSuggestion suggestion) {
- int currentUserId = mCallback.getCurrentUserId();
- ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ConfigurationInternal currentUserConfig =
+ mEnvironment.getConfigurationInternal(currentUserId);
if (DBG) {
Slog.d(LOG_TAG, "Telephony suggestion received. currentUserConfig=" + currentUserConfig
+ " newSuggestion=" + suggestion);
@@ -423,7 +427,7 @@
String zoneId;
// Introduce bias towards the device's current zone when there are multiple zone suggested.
- String deviceTimeZone = mCallback.getDeviceTimeZone();
+ String deviceTimeZone = mEnvironment.getDeviceTimeZone();
if (zoneIds.contains(deviceTimeZone)) {
if (DBG) {
Slog.d(LOG_TAG,
@@ -486,7 +490,7 @@
@GuardedBy("this")
private void setDeviceTimeZoneIfRequired(@NonNull String newZoneId, @NonNull String cause) {
- String currentZoneId = mCallback.getDeviceTimeZone();
+ String currentZoneId = mEnvironment.getDeviceTimeZone();
// Avoid unnecessary changes / intents.
if (newZoneId.equals(currentZoneId)) {
@@ -501,7 +505,7 @@
return;
}
- mCallback.setDeviceTimeZone(newZoneId);
+ mEnvironment.setDeviceTimeZone(newZoneId);
String msg = "Set device time zone."
+ ", currentZoneId=" + currentZoneId
+ ", newZoneId=" + newZoneId
@@ -572,8 +576,9 @@
// This method is called whenever the user changes or the config for any user changes. We
// don't know what happened, so we capture the current user's config, check to see if we
// need to clear state associated with a previous user, and rerun detection.
- int currentUserId = mCallback.getCurrentUserId();
- ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ConfigurationInternal currentUserConfig =
+ mEnvironment.getConfigurationInternal(currentUserId);
GeolocationTimeZoneSuggestion latestGeoLocationSuggestion =
mLatestGeoLocationSuggestion.get();
@@ -603,14 +608,14 @@
ipw.println("TimeZoneDetectorStrategy:");
ipw.increaseIndent(); // level 1
- int currentUserId = mCallback.getCurrentUserId();
- ipw.println("mCallback.getCurrentUserId()=" + currentUserId);
- ConfigurationInternal configuration = mCallback.getConfigurationInternal(currentUserId);
- ipw.println("mCallback.getConfiguration(currentUserId)=" + configuration);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ipw.println("mEnvironment.getCurrentUserId()=" + currentUserId);
+ ConfigurationInternal configuration = mEnvironment.getConfigurationInternal(currentUserId);
+ ipw.println("mEnvironment.getConfiguration(currentUserId)=" + configuration);
ipw.println("[Capabilities=" + configuration.createCapabilitiesAndConfig() + "]");
- ipw.println("mCallback.isDeviceTimeZoneInitialized()="
- + mCallback.isDeviceTimeZoneInitialized());
- ipw.println("mCallback.getDeviceTimeZone()=" + mCallback.getDeviceTimeZone());
+ ipw.println("mEnvironment.isDeviceTimeZoneInitialized()="
+ + mEnvironment.isDeviceTimeZoneInitialized());
+ ipw.println("mEnvironment.getDeviceTimeZone()=" + mEnvironment.getDeviceTimeZone());
ipw.println("Time zone change log:");
ipw.increaseIndent(); // level 2
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
index 83b33ee..a54288f 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import com.android.server.LocalServices;
+import com.android.server.timedetector.DeviceConfig;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
@@ -32,18 +33,22 @@
*/
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
- private static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
- private static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
- private static final Duration PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
+ private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
+ private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
+ Duration.ofMinutes(1);
+ private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
@NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
@NonNull private final LocationTimeZoneProviderController mController;
+ @NonNull private final DeviceConfig mDeviceConfig;
@NonNull private final ConfigurationChangeListener mConfigurationChangeListener;
ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
+ @NonNull DeviceConfig deviceConfig,
@NonNull LocationTimeZoneProviderController controller) {
super(threadingDomain);
mController = Objects.requireNonNull(controller);
+ mDeviceConfig = Objects.requireNonNull(deviceConfig);
mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
// Listen for configuration changes.
@@ -66,18 +71,24 @@
@Override
@NonNull
Duration getProviderInitializationTimeout() {
- return PROVIDER_INITIALIZATION_TIMEOUT;
+ return mDeviceConfig.getDurationFromMillis(
+ DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+ DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
}
@Override
@NonNull
Duration getProviderInitializationTimeoutFuzz() {
- return PROVIDER_INITIALIZATION_TIMEOUT_FUZZ;
+ return mDeviceConfig.getDurationFromMillis(
+ DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+ DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
}
@Override
@NonNull
Duration getUncertaintyDelay() {
- return PROVIDER_UNCERTAINTY_DELAY;
+ return mDeviceConfig.getDurationFromMillis(
+ DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+ DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 5bee7ee..364eaf8 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -43,6 +43,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.SystemService;
+import com.android.server.timedetector.DeviceConfig;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
import com.android.server.timezonedetector.TimeZoneDetectorService;
@@ -74,11 +75,17 @@
* mode" where the real binder clients are replaced by {@link
* SimulatedLocationTimeZoneProviderProxy}. This means that the real client providers are never
* bound (ensuring no real location events will be received) and simulated events / behaviors
- * can be injected via the command line. To enter simulation mode for a provider, use
- * "{@code adb shell setprop persist.sys.location_tz_simulation_mode.<provider name> 1}" and reboot.
- * e.g. "{@code adb shell setprop persist.sys.location_tz_simulation_mode.primary 1}}"
- * Then use "{@code adb shell cmd location_time_zone_manager help}" for injection. Set the system
- * properties to "0" and reboot to return to exit simulation mode.
+ * can be injected via the command line.
+ *
+ * <p>To enter simulation mode for a provider, use {@code adb shell cmd location_time_zone_manager
+ * set_provider_mode_override <provider name> simulated} and restart the service with {@code
+ * adb shell cmd location_time_zone_manager stop} and {@code adb shell cmd
+ * location_time_zone_manager start}.
+ *
+ * <p>e.g. {@code adb shell cmd location_time_zone_manager set_provider_mode_override primary
+ * simulated}.
+ *
+ * <p>See {@code adb shell cmd location_time_zone_manager help}" for more options.
*/
public class LocationTimeZoneManagerService extends Binder {
@@ -96,7 +103,7 @@
@Override
public void onStart() {
Context context = getContext();
- if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionEnabled(context)) {
+ if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
mService = new LocationTimeZoneManagerService(context);
// The service currently exposes no LocalService or Binder API, but it extends
@@ -110,7 +117,7 @@
@Override
public void onBootPhase(int phase) {
Context context = getContext();
- if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionEnabled(context)) {
+ if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// The location service must be functioning after this boot phase.
mService.onSystemReady();
@@ -221,10 +228,10 @@
LocationTimeZoneProvider secondary = createSecondaryProvider();
mLocationTimeZoneDetectorController =
new ControllerImpl(mThreadingDomain, primary, secondary);
- ControllerCallbackImpl callback = new ControllerCallbackImpl(
- mThreadingDomain);
+ DeviceConfig deviceConfig = new DeviceConfig();
mEnvironment = new ControllerEnvironmentImpl(
- mThreadingDomain, mLocationTimeZoneDetectorController);
+ mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController);
+ ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
mLocationTimeZoneDetectorController.initialize(mEnvironment, callback);
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 38ae51f..83085cc 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -977,7 +977,7 @@
AudioPortConfig sourceConfig = mAudioSource.activeConfig();
List<AudioPortConfig> sinkConfigs = new ArrayList<>();
AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
- boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
+ boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated || mAudioPatch == null;
for (AudioDevicePort audioSink : mAudioSink) {
AudioPortConfig sinkConfig = audioSink.activeConfig();
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index 6427ae2..fd12c2d2 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -18,16 +18,28 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkCapabilities.NetCapability;
+import android.net.NetworkRequest;
+import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
import android.os.ParcelUuid;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Tracks a set of Networks underpinning a VcnGatewayConnection.
@@ -38,53 +50,385 @@
*
* @hide
*/
-public class UnderlyingNetworkTracker extends Handler {
+public class UnderlyingNetworkTracker {
@NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final UnderlyingNetworkTrackerCallback mCb;
@NonNull private final Dependencies mDeps;
+ @NonNull private final Handler mHandler;
+ @NonNull private final ConnectivityManager mConnectivityManager;
+ @NonNull private final SubscriptionManager mSubscriptionManager;
+
+ @NonNull private final SparseArray<NetworkCallback> mCellBringupCallbacks = new SparseArray<>();
+ @NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback();
+ @NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
+
+ @NonNull private final Set<Integer> mSubIds = new ArraySet<>();
+
+ @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
+
+ @Nullable private UnderlyingNetworkRecord mCurrentRecord;
+ @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
public UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb) {
- this(vcnContext, subscriptionGroup, cb, new Dependencies());
+ this(
+ vcnContext,
+ subscriptionGroup,
+ requiredUnderlyingNetworkCapabilities,
+ cb,
+ new Dependencies());
}
private UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb,
@NonNull Dependencies deps) {
- super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
- mVcnContext = vcnContext;
+ mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ mRequiredUnderlyingNetworkCapabilities =
+ Objects.requireNonNull(
+ requiredUnderlyingNetworkCapabilities,
+ "Missing requiredUnderlyingNetworkCapabilities");
mCb = Objects.requireNonNull(cb, "Missing cb");
mDeps = Objects.requireNonNull(deps, "Missing deps");
+
+ mHandler = new Handler(mVcnContext.getLooper());
+
+ mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
+ mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class);
+
+ registerNetworkRequests();
+ }
+
+ private void registerNetworkRequests() {
+ // register bringup requests for underlying Networks
+ mConnectivityManager.requestBackgroundNetwork(
+ getWifiNetworkRequest(), mHandler, mWifiBringupCallback);
+ updateSubIdsAndCellularRequests();
+
+ // register Network-selection request used to decide selected underlying Network
+ mConnectivityManager.requestBackgroundNetwork(
+ getNetworkRequestBase().build(), mHandler, mRouteSelectionCallback);
+ }
+
+ private NetworkRequest getWifiNetworkRequest() {
+ return getNetworkRequestBase().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+ }
+
+ private NetworkRequest getCellNetworkRequestForSubId(int subId) {
+ return getNetworkRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
+ .build();
+ }
+
+ private NetworkRequest.Builder getNetworkRequestBase() {
+ NetworkRequest.Builder requestBase = new NetworkRequest.Builder();
+ for (@NetCapability int capability : mRequiredUnderlyingNetworkCapabilities) {
+ requestBase.addCapability(capability);
+ }
+
+ return requestBase
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ /**
+ * Update the current subIds and Cellular bringup requests for this UnderlyingNetworkTracker.
+ */
+ private void updateSubIdsAndCellularRequests() {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ Set<Integer> prevSubIds = new ArraySet<>(mSubIds);
+ mSubIds.clear();
+
+ // Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup
+ // STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony
+ List<SubscriptionInfo> subInfos =
+ mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup);
+
+ for (SubscriptionInfo subInfo : subInfos) {
+ final int subId = subInfo.getSubscriptionId();
+ mSubIds.add(subId);
+
+ if (!mCellBringupCallbacks.contains(subId)) {
+ final NetworkBringupCallback cb = new NetworkBringupCallback();
+ mCellBringupCallbacks.put(subId, cb);
+
+ mConnectivityManager.requestBackgroundNetwork(
+ getCellNetworkRequestForSubId(subId), mHandler, cb);
+ }
+ }
+
+ // unregister all NetworkCallbacks for outdated subIds
+ for (final int subId : prevSubIds) {
+ if (!mSubIds.contains(subId)) {
+ final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
+ mConnectivityManager.unregisterNetworkCallback(cb);
+ }
+ }
}
/** Tears down this Tracker, and releases all underlying network requests. */
- public void teardown() {}
+ public void teardown() {
+ mVcnContext.ensureRunningOnLooperThread();
- /** An record of a single underlying network, caching relevant fields. */
+ mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback);
+ mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback);
+
+ for (final int subId : mSubIds) {
+ final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
+ mConnectivityManager.unregisterNetworkCallback(cb);
+ }
+ mSubIds.clear();
+ }
+
+ /** Returns whether the currently selected Network matches the given network. */
+ private static boolean isSameNetwork(
+ @Nullable UnderlyingNetworkRecord.Builder recordInProgress, @NonNull Network network) {
+ return recordInProgress != null && recordInProgress.getNetwork().equals(network);
+ }
+
+ /** Notify the Callback if a full UnderlyingNetworkRecord exists. */
+ private void maybeNotifyCallback() {
+ // Only forward this update if a complete record has been received
+ if (!mRecordInProgress.isValid()) {
+ return;
+ }
+
+ // Only forward this update if the updated record differs form the current record
+ UnderlyingNetworkRecord updatedRecord = mRecordInProgress.build();
+ if (!updatedRecord.equals(mCurrentRecord)) {
+ mCurrentRecord = updatedRecord;
+
+ mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
+ }
+ }
+
+ private void handleNetworkAvailable(@NonNull Network network) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ mRecordInProgress = new UnderlyingNetworkRecord.Builder(network);
+ }
+
+ private void handleNetworkLost(@NonNull Network network) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Non-underlying Network lost");
+ return;
+ }
+
+ mRecordInProgress = null;
+ mCurrentRecord = null;
+ mCb.onSelectedUnderlyingNetworkChanged(null /* underlyingNetworkRecord */);
+ }
+
+ private void handleCapabilitiesChanged(
+ @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to NetworkCapabilities");
+ return;
+ }
+
+ mRecordInProgress.setNetworkCapabilities(networkCapabilities);
+
+ maybeNotifyCallback();
+ }
+
+ private void handleNetworkSuspended(@NonNull Network network, boolean isSuspended) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to isSuspended");
+ return;
+ }
+
+ final NetworkCapabilities newCaps =
+ new NetworkCapabilities(mRecordInProgress.getNetworkCapabilities());
+ if (isSuspended) {
+ newCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
+ } else {
+ newCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
+ }
+
+ handleCapabilitiesChanged(network, newCaps);
+ }
+
+ private void handlePropertiesChanged(
+ @NonNull Network network, @NonNull LinkProperties linkProperties) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to LinkProperties");
+ return;
+ }
+
+ mRecordInProgress.setLinkProperties(linkProperties);
+
+ maybeNotifyCallback();
+ }
+
+ private void handleNetworkBlocked(@NonNull Network network, boolean isBlocked) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to isBlocked");
+ return;
+ }
+
+ mRecordInProgress.setIsBlocked(isBlocked);
+
+ maybeNotifyCallback();
+ }
+
+ /**
+ * NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped.
+ *
+ * <p>NetworkBringupCallback only exists to prevent matching (VCN-managed) Networks from being
+ * reaped, and no action is taken on any events firing.
+ */
+ @VisibleForTesting
+ class NetworkBringupCallback extends NetworkCallback {}
+
+ /**
+ * RouteSelectionCallback is used to select the "best" underlying Network.
+ *
+ * <p>The "best" network is determined by ConnectivityService, which is treated as a source of
+ * truth.
+ */
+ @VisibleForTesting
+ class RouteSelectionCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ handleNetworkAvailable(network);
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ handleNetworkLost(network);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(
+ @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
+ handleCapabilitiesChanged(network, networkCapabilities);
+ }
+
+ @Override
+ public void onNetworkSuspended(@NonNull Network network) {
+ handleNetworkSuspended(network, true /* isSuspended */);
+ }
+
+ @Override
+ public void onNetworkResumed(@NonNull Network network) {
+ handleNetworkSuspended(network, false /* isSuspended */);
+ }
+
+ @Override
+ public void onLinkPropertiesChanged(
+ @NonNull Network network, @NonNull LinkProperties linkProperties) {
+ handlePropertiesChanged(network, linkProperties);
+ }
+
+ @Override
+ public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
+ handleNetworkBlocked(network, isBlocked);
+ }
+ }
+
+ /** A record of a single underlying network, caching relevant fields. */
public static class UnderlyingNetworkRecord {
@NonNull public final Network network;
@NonNull public final NetworkCapabilities networkCapabilities;
@NonNull public final LinkProperties linkProperties;
- public final boolean blocked;
+ public final boolean isBlocked;
@VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
- boolean blocked) {
+ boolean isBlocked) {
this.network = network;
this.networkCapabilities = networkCapabilities;
this.linkProperties = linkProperties;
- this.blocked = blocked;
+ this.isBlocked = isBlocked;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof UnderlyingNetworkRecord)) return false;
+ final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o;
+
+ return network.equals(that.network)
+ && networkCapabilities.equals(that.networkCapabilities)
+ && linkProperties.equals(that.linkProperties)
+ && isBlocked == that.isBlocked;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
+ }
+
+ /** Builder to incrementally construct an UnderlyingNetworkRecord. */
+ private static class Builder {
+ @NonNull private final Network mNetwork;
+
+ @Nullable private NetworkCapabilities mNetworkCapabilities;
+ @Nullable private LinkProperties mLinkProperties;
+ boolean mIsBlocked;
+ boolean mWasIsBlockedSet;
+
+ private Builder(@NonNull Network network) {
+ mNetwork = network;
+ }
+
+ @NonNull
+ private Network getNetwork() {
+ return mNetwork;
+ }
+
+ private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ }
+
+ @Nullable
+ private NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
+
+ private void setLinkProperties(@NonNull LinkProperties linkProperties) {
+ mLinkProperties = linkProperties;
+ }
+
+ private void setIsBlocked(boolean isBlocked) {
+ mIsBlocked = isBlocked;
+ mWasIsBlockedSet = true;
+ }
+
+ private boolean isValid() {
+ return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
+ }
+
+ private UnderlyingNetworkRecord build() {
+ return new UnderlyingNetworkRecord(
+ mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
+ }
}
}
@@ -95,9 +439,10 @@
*
* <p>This callback does NOT signal a mobility event.
*
- * @param underlying The details of the new underlying network
+ * @param underlyingNetworkRecord The details of the new underlying network
*/
- void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying);
+ void onSelectedUnderlyingNetworkChanged(
+ @Nullable UnderlyingNetworkRecord underlyingNetworkRecord);
}
private static class Dependencies {}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 9d21b92..fd19322 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -168,8 +168,8 @@
@NonNull NetworkRequest request, int score, int providerId) {
if (score > getNetworkScore()) {
Slog.v(getLogTag(),
- "Request " + request.requestId + " already satisfied by higher-scoring ("
- + score + ") network from provider " + providerId);
+ "Request already satisfied by higher-scoring (" + score + ") network from "
+ + "provider " + providerId + ": " + request);
return;
}
@@ -177,8 +177,7 @@
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(getLogTag(),
- "Request " + request.requestId
- + " satisfied by existing VcnGatewayConnection");
+ "Request already satisfied by existing VcnGatewayConnection: " + request);
return;
}
}
@@ -202,12 +201,12 @@
private boolean requestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
- final NetworkCapabilities configCaps = new NetworkCapabilities();
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
for (int cap : config.getAllExposedCapabilities()) {
- configCaps.addCapability(cap);
+ builder.addCapability(cap);
}
- return request.networkCapabilities.satisfiedByNetworkCapabilities(configCaps);
+ return request.canBeSatisfiedBy(builder.build());
}
private String getLogTag() {
@@ -215,7 +214,8 @@
}
/** Retrieves the network score for a VCN Network */
- private int getNetworkScore() {
+ // Package visibility for use in VcnGatewayConnection
+ static int getNetworkScore() {
// TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in
// subGrp" value
return 52;
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index dba59bd..7399e56 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -55,4 +55,15 @@
public VcnNetworkProvider getVcnNetworkProvider() {
return mVcnNetworkProvider;
}
+
+ /**
+ * Verifies that the caller is running on the VcnContext Thread.
+ *
+ * @throwsIllegalStateException if the caller is not running on the VcnContext Thread.
+ */
+ public void ensureRunningOnLooperThread() {
+ if (getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException("Not running on VcnMgmtSvc thread");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 703bfab..db37227 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -17,8 +17,11 @@
package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static com.android.server.VcnManagementService.VDBG;
@@ -34,8 +37,10 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
import android.net.annotations.PolicyDirection;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
@@ -47,10 +52,13 @@
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -64,7 +72,9 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.util.Arrays;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -112,6 +122,9 @@
public class VcnGatewayConnection extends StateMachine {
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
+ private static final int[] MERGED_CAPABILITIES =
+ new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING};
+
private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE;
@@ -476,7 +489,10 @@
mUnderlyingNetworkTracker =
mDeps.newUnderlyingNetworkTracker(
- mVcnContext, subscriptionGroup, mUnderlyingNetworkTrackerCallback);
+ mVcnContext,
+ subscriptionGroup,
+ mConnectionConfig.getAllUnderlyingCapabilities(),
+ mUnderlyingNetworkTrackerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
IpSecTunnelInterface iface;
@@ -533,6 +549,8 @@
@Override
public void onSelectedUnderlyingNetworkChanged(
@Nullable UnderlyingNetworkRecord underlying) {
+ // TODO(b/179091925): Move the delayed-message handling to BaseState
+
// If underlying is null, all underlying networks have been lost. Disconnect VCN after a
// timeout.
if (underlying == null) {
@@ -917,6 +935,8 @@
transitionTo(mDisconnectingState);
break;
case EVENT_SESSION_CLOSED:
+ // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+ // message may not be posted again. Defer to ensure immediate shutdown.
deferMessage(msg);
transitionTo(mDisconnectingState);
@@ -937,7 +957,108 @@
}
}
- private abstract class ConnectedStateBase extends ActiveBaseState {}
+ private abstract class ConnectedStateBase extends ActiveBaseState {
+ protected void updateNetworkAgent(
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull NetworkAgent agent,
+ @NonNull ChildSessionConfiguration childConfig) {
+ final NetworkCapabilities caps =
+ buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+ final LinkProperties lp =
+ buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+ agent.sendNetworkCapabilities(caps);
+ agent.sendLinkProperties(lp);
+ }
+
+ protected NetworkAgent buildNetworkAgent(
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig) {
+ final NetworkCapabilities caps =
+ buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+ final LinkProperties lp =
+ buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+ final NetworkAgent agent =
+ new NetworkAgent(
+ mVcnContext.getContext(),
+ mVcnContext.getLooper(),
+ TAG,
+ caps,
+ lp,
+ Vcn.getNetworkScore(),
+ new NetworkAgentConfig(),
+ mVcnContext.getVcnNetworkProvider()) {
+ @Override
+ public void unwanted() {
+ teardownAsynchronously();
+ }
+ };
+
+ agent.register();
+ agent.markConnected();
+
+ return agent;
+ }
+
+ protected void applyTransform(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull Network underlyingNetwork,
+ @NonNull IpSecTransform transform,
+ int direction) {
+ try {
+ // TODO: Set underlying network of tunnel interface
+
+ // Transforms do not need to be persisted; the IkeSession will keep them alive
+ mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
+ } catch (IOException e) {
+ Slog.d(TAG, "Transform application failed for network " + token, e);
+ sessionLost(token, e);
+ }
+ }
+
+ protected void setupInterface(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig) {
+ setupInterface(token, tunnelIface, childConfig, null);
+ }
+
+ protected void setupInterface(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig,
+ @Nullable ChildSessionConfiguration oldChildConfig) {
+ try {
+ final Set<LinkAddress> newAddrs =
+ new ArraySet<>(childConfig.getInternalAddresses());
+ final Set<LinkAddress> existingAddrs = new ArraySet<>();
+ if (oldChildConfig != null) {
+ existingAddrs.addAll(oldChildConfig.getInternalAddresses());
+ }
+
+ final Set<LinkAddress> toAdd = new ArraySet<>();
+ toAdd.addAll(newAddrs);
+ toAdd.removeAll(existingAddrs);
+
+ final Set<LinkAddress> toRemove = new ArraySet<>();
+ toRemove.addAll(existingAddrs);
+ toRemove.removeAll(newAddrs);
+
+ for (LinkAddress address : toAdd) {
+ tunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
+ }
+
+ for (LinkAddress address : toRemove) {
+ tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength());
+ }
+ } catch (IOException e) {
+ Slog.d(TAG, "Adding address to tunnel failed for token " + token, e);
+ sessionLost(token, e);
+ }
+ }
+ }
/**
* Stable state representing a VCN that has a functioning connection to the mobility anchor.
@@ -947,7 +1068,89 @@
*/
class ConnectedState extends ConnectedStateBase {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ // Successful connection, clear failed attempt counter
+ mFailedAttempts = 0;
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ handleUnderlyingNetworkChanged(msg);
+ break;
+ case EVENT_SESSION_CLOSED:
+ // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+ // message may not be posted again. Defer to ensure immediate shutdown.
+ deferMessage(msg);
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_SESSION_LOST:
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_TRANSFORM_CREATED:
+ final EventTransformCreatedInfo transformCreatedInfo =
+ (EventTransformCreatedInfo) msg.obj;
+
+ applyTransform(
+ mCurrentToken,
+ mTunnelIface,
+ mUnderlying.network,
+ transformCreatedInfo.transform,
+ transformCreatedInfo.direction);
+ break;
+ case EVENT_SETUP_COMPLETED:
+ mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
+
+ setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
+
+ private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
+ final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ if (mUnderlying == null) {
+ // Ignored for now; a new network may be coming up. If none does, the delayed
+ // NETWORK_LOST disconnect will be fired, and tear down the session + network.
+ return;
+ }
+
+ // mUnderlying assumed non-null, given check above.
+ // If network changed, migrate. Otherwise, update any existing networkAgent.
+ if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
+ mIkeSession.setNetwork(mUnderlying.network);
+ } else {
+ // oldUnderlying is non-null & underlying network itself has not changed
+ // (only network properties were changed).
+
+ // Network not yet set up, or child not yet connected.
+ if (mNetworkAgent != null && mChildConfig != null) {
+ // If only network properties changed and agent is active, update properties
+ updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+ }
+ }
+ }
+
+ protected void setupInterfaceAndNetworkAgent(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig) {
+ setupInterface(token, tunnelIface, childConfig);
+
+ if (mNetworkAgent == null) {
+ mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
+ } else {
+ updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+ }
+ }
}
/**
@@ -962,19 +1165,66 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
static NetworkCapabilities buildNetworkCapabilities(
- @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
- final NetworkCapabilities caps = new NetworkCapabilities();
+ @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
+ @Nullable UnderlyingNetworkRecord underlying) {
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
- caps.addTransportType(TRANSPORT_CELLULAR);
- caps.addCapability(NET_CAPABILITY_NOT_CONGESTED);
- caps.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ builder.addTransportType(TRANSPORT_CELLULAR);
+ builder.addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ builder.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
// Add exposed capabilities
for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) {
- caps.addCapability(cap);
+ builder.addCapability(cap);
}
- return caps;
+ if (underlying != null) {
+ final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
+
+ // Mirror merged capabilities.
+ for (int cap : MERGED_CAPABILITIES) {
+ if (underlyingCaps.hasCapability(cap)) {
+ builder.addCapability(cap);
+ }
+ }
+
+ // Set admin UIDs for ConnectivityDiagnostics use.
+ final int[] underlyingAdminUids = underlyingCaps.getAdministratorUids();
+ Arrays.sort(underlyingAdminUids); // Sort to allow contains check below.
+
+ final int[] adminUids;
+ if (underlyingCaps.getOwnerUid() > 0 // No owner UID specified
+ && 0 > Arrays.binarySearch(// Owner UID not found in admin UID list.
+ underlyingAdminUids, underlyingCaps.getOwnerUid())) {
+ adminUids = Arrays.copyOf(underlyingAdminUids, underlyingAdminUids.length + 1);
+ adminUids[adminUids.length - 1] = underlyingCaps.getOwnerUid();
+ Arrays.sort(adminUids);
+ } else {
+ adminUids = underlyingAdminUids;
+ }
+ builder.setAdministratorUids(adminUids);
+
+ // Set TransportInfo for SysUI use (never parcelled out of SystemServer).
+ if (underlyingCaps.hasTransport(TRANSPORT_WIFI)
+ && underlyingCaps.getTransportInfo() instanceof WifiInfo) {
+ final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo();
+ builder.setTransportInfo(new VcnTransportInfo(wifiInfo));
+ } else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR)
+ && underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
+ final TelephonyNetworkSpecifier telNetSpecifier =
+ (TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier();
+ builder.setTransportInfo(new VcnTransportInfo(telNetSpecifier.getSubscriptionId()));
+ } else {
+ Slog.wtf(
+ TAG,
+ "Unknown transport type or missing TransportInfo/NetworkSpecifier for"
+ + " non-null underlying network");
+ }
+ }
+
+ // TODO: Make a VcnNetworkSpecifier, and match all underlying subscription IDs.
+
+ return builder.build();
}
private static LinkProperties buildConnectedLinkProperties(
@@ -1134,8 +1384,10 @@
public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
+ Set<Integer> requiredUnderlyingNetworkCapabilities,
UnderlyingNetworkTrackerCallback callback) {
- return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback);
+ return new UnderlyingNetworkTracker(
+ vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback);
}
/** Builds a new IkeSession. */
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index 7f5b23c..b9babae 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -21,9 +21,9 @@
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.os.Looper;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
-import android.util.SparseArray;
import java.util.Objects;
import java.util.Set;
@@ -40,7 +40,13 @@
private static final String TAG = VcnNetworkProvider.class.getSimpleName();
private final Set<NetworkRequestListener> mListeners = new ArraySet<>();
- private final SparseArray<NetworkRequestEntry> mRequests = new SparseArray<>();
+
+ /**
+ * Cache of NetworkRequest(s), scores and network providers, keyed by NetworkRequest
+ *
+ * <p>NetworkRequests are immutable once created, and therefore can be used as stable keys.
+ */
+ private final ArrayMap<NetworkRequest, NetworkRequestEntry> mRequests = new ArrayMap<>();
public VcnNetworkProvider(Context context, Looper looper) {
super(context, looper, VcnNetworkProvider.class.getSimpleName());
@@ -51,8 +57,8 @@
mListeners.add(listener);
// Send listener all cached requests
- for (int i = 0; i < mRequests.size(); i++) {
- notifyListenerForEvent(listener, mRequests.valueAt(i));
+ for (NetworkRequestEntry entry : mRequests.values()) {
+ notifyListenerForEvent(listener, entry);
}
}
@@ -75,7 +81,9 @@
request, score, providerId));
final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId);
- mRequests.put(request.requestId, entry);
+
+ // NetworkRequests are immutable once created, and therefore can be used as stable keys.
+ mRequests.put(request, entry);
// TODO(b/176939047): Intelligently route requests to prioritized VcnInstances (based on
// Default Data Sub, or similar)
@@ -86,7 +94,7 @@
@Override
public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
- mRequests.remove(request.requestId);
+ mRequests.remove(request);
}
private static class NetworkRequestEntry {
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index fe3b03ab..e0f5408 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -138,6 +138,11 @@
return mStatus != Status.RUNNING;
}
+ /** Return true is effect is a repeating vibration. */
+ public boolean isRepeating() {
+ return mEffect.getDuration() == Long.MAX_VALUE;
+ }
+
/** Return the effect that should be played by this vibration. */
@Nullable
public CombinedVibrationEffect getEffect() {
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 536375f..8910bdf 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -34,10 +34,12 @@
import android.os.Vibrator;
import android.provider.Settings;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.VibratorServiceDumpProto;
import java.util.ArrayList;
import java.util.List;
@@ -340,6 +342,24 @@
+ '}';
}
+ /** Write current settings into given {@link ProtoOutputStream}. */
+ public void dumpProto(ProtoOutputStream proto) {
+ synchronized (mLock) {
+ proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
+ mHapticFeedbackIntensity);
+ proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
+ mVibrator.getDefaultHapticFeedbackIntensity());
+ proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
+ mNotificationIntensity);
+ proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
+ mVibrator.getDefaultNotificationVibrationIntensity());
+ proto.write(VibratorServiceDumpProto.RING_INTENSITY,
+ mRingIntensity);
+ proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
+ mVibrator.getDefaultRingVibrationIntensity());
+ }
+ }
+
private void notifyListeners() {
List<OnVibratorSettingsChanged> currentListeners;
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 5355252..4f2fc86 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -114,12 +114,11 @@
}
}
- public Vibration getVibration() {
- return mVibration;
- }
-
@Override
public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Binder died, cancelling vibration...");
+ }
cancel();
}
@@ -150,12 +149,19 @@
/** Notify current vibration that a step has completed on given vibrator. */
public void vibratorComplete(int vibratorId) {
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
+ }
if (mCurrentVibrateStep != null) {
mCurrentVibrateStep.vibratorComplete(vibratorId);
}
}
}
+ public Vibration getVibration() {
+ return mVibration;
+ }
+
@VisibleForTesting
SparseArray<VibratorController> getVibrators() {
return mVibrators;
@@ -467,7 +473,7 @@
noteVibratorOff();
}
if (DEBUG) {
- Slog.d(TAG, "SingleVibrateStep step done.");
+ Slog.d(TAG, "SingleVibrateStep done.");
}
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 5fe853a..ce951b8 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -54,6 +54,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -100,6 +101,17 @@
}
@Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(
+ "ActivityClientController", e);
+ }
+ }
+
+ @Override
public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3bb4c74..36c5037 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -81,6 +82,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -201,9 +203,13 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
@@ -249,6 +255,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -266,6 +273,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -303,13 +311,13 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
+import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.AttributeCache;
import com.android.server.LocalServices;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.PendingIntentRecord;
@@ -321,6 +329,7 @@
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.Task.ActivityState;
import com.android.server.wm.WindowManagerService.H;
+import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
import com.android.server.wm.utils.InsetUtils;
import com.google.android.collect.Sets;
@@ -859,6 +868,9 @@
pw.print(Integer.toHexString(taskDescription.getStatusBarColor()));
pw.print(" navigationBarColor=");
pw.println(Integer.toHexString(taskDescription.getNavigationBarColor()));
+ pw.print(" backgroundColorFloating=");
+ pw.println(Integer.toHexString(
+ taskDescription.getBackgroundColorFloating()));
}
}
if (results != null) {
@@ -1361,7 +1373,8 @@
if (mLetterbox == null) {
mLetterbox = new Letterbox(() -> makeChildSurface(null),
mWmService.mTransactionFactory,
- mWmService::isLetterboxActivityCornersRounded);
+ mWmService::isLetterboxActivityCornersRounded,
+ this::getLetterboxBackgroundColor);
mLetterbox.attachInput(w);
}
getPosition(mTmpPoint);
@@ -1381,6 +1394,31 @@
}
}
+ private Color getLetterboxBackgroundColor() {
+ final WindowState w = findMainWindow();
+ if (w == null || w.isLetterboxedForDisplayCutout()) {
+ return Color.valueOf(Color.BLACK);
+ }
+ @LetterboxBackgroundType int letterboxBackgroundType =
+ mWmService.getLetterboxBackgroundType();
+ switch (letterboxBackgroundType) {
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColorFloating());
+ }
+ return mWmService.getLetterboxBackgroundColor();
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColor());
+ }
+ // Falling through
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ return mWmService.getLetterboxBackgroundColor();
+ }
+ throw new AssertionError(
+ "Unexpected letterbox background type: " + letterboxBackgroundType);
+ }
+
/** @return {@code true} when main window is letterboxed and activity isn't transparent. */
private boolean isLetterboxed(WindowState mainWindow) {
return mainWindow.isLetterboxedAppWindow() && fillsParent();
@@ -1431,14 +1469,11 @@
}
/**
- * @return {@code true} if bar shown within a given rectangle is allowed to be transparent
+ * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
* when the current activity is displayed.
*/
- boolean isTransparentBarAllowed(Rect rect) {
- // TODO(b/175482966): Allow status and navigation bars to be semi-transparent black
- // in letterbox mode.
- return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect)
- || mWmService.isLetterboxActivityCornersRounded();
+ boolean isFullyTransparentBarAllowed(Rect rect) {
+ return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
}
/**
@@ -2183,6 +2218,7 @@
}
/** @return Root task of this activity, null if there is no task. */
+ @Nullable
Task getRootTask() {
return task != null ? task.getRootTask() : null;
}
@@ -2191,6 +2227,12 @@
return task != null ? task.getRootTaskId() : INVALID_TASK_ID;
}
+ /** @return the first organized parent task. */
+ @Nullable
+ Task getOrganizedTask() {
+ return task != null ? task.getOrganizedTask() : null;
+ }
+
@Override
@Nullable
TaskDisplayArea getDisplayArea() {
@@ -6733,6 +6775,20 @@
// layout traversals.
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+
+ // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
+ // size compat mode.
+ if (providesMaxBounds()) {
+ if (DEBUG_CONFIGURATION) {
+ ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
+ + "due to letterboxing? %s mismatch with parent bounds? %s size compat "
+ + "mode %s", getUid(),
+ resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null,
+ !matchParentBounds(), inSizeCompatMode());
+ }
+ resolvedConfig.windowConfiguration
+ .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
+ }
}
/**
@@ -6916,6 +6972,19 @@
return super.getBounds();
}
+ @Override
+ public boolean providesMaxBounds() {
+ // System and SystemUI should always be able to access the physical display bounds,
+ // so do not provide it with the overridden maximum bounds.
+ // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
+ if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
+ getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
+ return false;
+ }
+ // Max bounds should be sandboxed when this is letterboxed or in size compat mode.
+ return mLetterbox != null || !matchParentBounds() || inSizeCompatMode();
+ }
+
@VisibleForTesting
@Override
Rect getAnimationBounds(int appRootTaskClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c6ed16c..d846c3a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2098,13 +2098,6 @@
final ActivityRecord top = targetTask.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
- // The above code can remove {@code reusedActivity} from the task, leading to the
- // {@code ActivityRecord} removing its reference to the {@code Task}. The task
- // reference is needed in the call below to {@link setTargetStackAndMoveToFrontIfNeeded}
- if (targetTaskTop.getTask() == null) {
- targetTask.addChild(targetTaskTop);
- }
-
if (top != null) {
if (top.isRootOfTask()) {
// Activity aliases may mean we use different intents for the top activity,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 081141c..7d2075c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -279,12 +279,6 @@
public abstract void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);
/**
- * This enforces {@code func} can only be called if either the caller is Recents activity or
- * has {@code permission}.
- */
- public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func);
-
- /**
* Returns true if the app can close system dialogs. Otherwise it either throws a {@link
* SecurityException} or returns false with a logcat message depending on whether the app
* targets SDK level {@link android.os.Build.VERSION_CODES#S} or not.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 404773d..2d6e9b2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -232,13 +232,13 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.TransferPipe;
+import com.android.internal.policy.AttributeCache;
import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.AttributeCache;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
@@ -486,13 +486,13 @@
* Whether normal application switches are allowed; a call to {@link #stopAppSwitches()
* disables this.
*/
- private boolean mAppSwitchesAllowed = true;
+ private volatile boolean mAppSwitchesAllowed = true;
/**
* Last stop app switches time, apps finished before this time cannot start background activity
* even if they are in grace period.
*/
- private long mLastStopAppSwitchesTime;
+ private volatile long mLastStopAppSwitchesTime;
IActivityController mController = null;
boolean mControllerIsAMonkey = false;
@@ -698,6 +698,7 @@
int OOM_ADJUSTMENT = 1;
int LRU_UPDATE = 2;
int PROCESS_CHANGE = 3;
+ int START_SERVICE = 4;
int caller() default NONE;
}
@@ -1578,7 +1579,7 @@
@Override
public void startRecentsActivity(Intent intent, long eventTime,
@Nullable IRecentsAnimationRunner recentsAnimationRunner) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "startRecentsActivity()");
+ enforceTaskPermission("startRecentsActivity()");
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
@@ -1606,7 +1607,7 @@
@Override
public final int startActivityFromRecents(int taskId, Bundle bOptions) {
- enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
+ mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"startActivityFromRecents()");
final int callingPid = Binder.getCallingPid();
@@ -1736,7 +1737,7 @@
@Override
public RootTaskInfo getFocusedRootTaskInfo() throws RemoteException {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getFocusedRootTaskInfo()");
+ enforceTaskPermission("getFocusedRootTaskInfo()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -1797,7 +1798,7 @@
@Override
public boolean removeTask(int taskId) {
- enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
+ mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1822,7 +1823,7 @@
@Override
public void removeAllVisibleRecentTasks() {
- enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
+ mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1861,8 +1862,7 @@
@Override
public ActivityManager.TaskDescription getTaskDescription(int id) {
synchronized (mGlobalLock) {
- enforceCallerIsRecentsOrHasPermission(
- MANAGE_ACTIVITY_TASKS, "getTaskDescription()");
+ enforceTaskPermission("getTaskDescription()");
final Task tr = mRootWindowContainer.anyTaskForId(id,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (tr != null) {
@@ -1874,7 +1874,7 @@
@Override
public boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "setTaskWindowingMode()");
+ enforceTaskPermission("setTaskWindowingMode()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -2110,7 +2110,7 @@
@Override
public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "moveTaskToRootTask()");
+ enforceTaskPermission("moveTaskToRootTask()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -2199,8 +2199,7 @@
*/
@Override
public void removeRootTasksInWindowingModes(int[] windowingModes) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
- "removeRootTasksInWindowingModes()");
+ enforceTaskPermission("removeRootTasksInWindowingModes()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
@@ -2214,8 +2213,7 @@
@Override
public void removeRootTasksWithActivityTypes(int[] activityTypes) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
- "removeRootTasksWithActivityTypes()");
+ enforceTaskPermission("removeRootTasksWithActivityTypes()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
@@ -2241,7 +2239,7 @@
@Override
public List<RootTaskInfo> getAllRootTaskInfos() {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getAllRootTaskInfos()");
+ enforceTaskPermission("getAllRootTaskInfos()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2254,7 +2252,7 @@
@Override
public RootTaskInfo getRootTaskInfo(int windowingMode, int activityType) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getRootTaskInfo()");
+ enforceTaskPermission("getRootTaskInfo()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2267,8 +2265,7 @@
@Override
public List<RootTaskInfo> getAllRootTaskInfosOnDisplay(int displayId) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
- "getAllRootTaskInfosOnDisplay()");
+ enforceTaskPermission("getAllRootTaskInfosOnDisplay()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2282,7 +2279,7 @@
@Override
public RootTaskInfo getRootTaskInfoOnDisplay(int windowingMode, int activityType,
int displayId) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getRootTaskInfoOnDisplay()");
+ enforceTaskPermission("getRootTaskInfoOnDisplay()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2295,7 +2292,7 @@
@Override
public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "cancelRecentsAnimation()");
+ enforceTaskPermission("cancelRecentsAnimation()");
final long callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
@@ -2460,6 +2457,13 @@
if (referrer != null) {
pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
}
+ if (!pae.activity.isAttached()) {
+ // Skip directly because the caller activity may have been destroyed. If a caller
+ // is waiting for the assist data, it will be notified by timeout
+ // (see PendingAssistExtras#run()) and then pendingAssistExtrasTimedOut will clean
+ // up the request.
+ return;
+ }
if (structure != null) {
// Pre-fill the task/activity component for all assist data receivers
structure.setTaskId(pae.activity.getTask().mTaskId);
@@ -2730,16 +2734,14 @@
/** Sets the task stack listener that gets callbacks when a task stack changes. */
@Override
public void registerTaskStackListener(ITaskStackListener listener) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
- "registerTaskStackListener()");
+ enforceTaskPermission("registerTaskStackListener()");
mTaskChangeNotificationController.registerTaskStackListener(listener);
}
/** Unregister a task stack listener so that it stops receiving callbacks. */
@Override
public void unregisterTaskStackListener(ITaskStackListener listener) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
- "unregisterTaskStackListener()");
+ enforceTaskPermission("unregisterTaskStackListener()");
mTaskChangeNotificationController.unregisterTaskStackListener(listener);
}
@@ -2792,19 +2794,6 @@
permission, Binder.getCallingPid(), Binder.getCallingUid());
}
- /** This can be called with or without the global lock held. */
- void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
- if (getRecentTasks().isCallerRecents(Binder.getCallingUid())) {
- return;
- }
-
- if (permission.equals(MANAGE_ACTIVITY_TASKS) || permission.equals(MANAGE_ACTIVITY_STACKS)) {
- enforceTaskPermission(func);
- } else {
- mAmInternal.enforceCallingPermission(permission, func);
- }
- }
-
/**
* Returns true if the app can close system dialogs. Otherwise it either throws a {@link
* SecurityException} or returns false with a logcat message depending on whether the app
@@ -3265,7 +3254,7 @@
public void resizePrimarySplitScreen(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "resizePrimarySplitScreen()");
+ enforceTaskPermission("resizePrimarySplitScreen()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3303,7 +3292,7 @@
@Override
public void setSplitScreenResizing(boolean resizing) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "setSplitScreenResizing()");
+ enforceTaskPermission("setSplitScreenResizing()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3373,8 +3362,7 @@
@Override
public void cancelTaskWindowTransition(int taskId) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
- "cancelTaskWindowTransition()");
+ enforceTaskPermission("cancelTaskWindowTransition()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3393,7 +3381,7 @@
@Override
public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
- enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
+ mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
return getTaskSnapshot(taskId, isLowResolution, true /* restoreFromDisk */);
@@ -3528,7 +3516,7 @@
@Override
public void stopAppSwitches() {
- enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
+ mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches");
synchronized (mGlobalLock) {
mAppSwitchesAllowed = false;
mLastStopAppSwitchesTime = SystemClock.uptimeMillis();
@@ -3537,7 +3525,7 @@
@Override
public void resumeAppSwitches() {
- enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
+ mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
synchronized (mGlobalLock) {
mAppSwitchesAllowed = true;
}
@@ -4764,6 +4752,7 @@
}
/** A uid is considered to be foreground if it has a visible non-toast window. */
+ @HotPath(caller = HotPath.START_SERVICE)
boolean hasActiveVisibleWindow(int uid) {
if (mVisibleActivityProcessTracker.hasVisibleActivity(uid)) {
return true;
@@ -4860,15 +4849,21 @@
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
- if (!(e instanceof SecurityException)) {
- Slog.w(TAG, "Activity Task Manager onTransact aborts "
- + " UID:" + Binder.getCallingUid()
- + " PID:" + Binder.getCallingPid(), e);
- }
- throw e;
+ throw logAndRethrowRuntimeExceptionOnTransact(TAG, e);
}
}
+ /** Provides the full stack traces of non-security exception that occurs in onTransact. */
+ static RuntimeException logAndRethrowRuntimeExceptionOnTransact(String name,
+ RuntimeException e) {
+ if (!(e instanceof SecurityException)) {
+ Slog.w(TAG, name + " onTransact aborts"
+ + " UID:" + Binder.getCallingUid()
+ + " PID:" + Binder.getCallingPid(), e);
+ }
+ throw e;
+ }
+
/**
* Sets the corresponding {@link DisplayArea} information for the process global
* configuration. To be called when we need to show IME on a different {@link DisplayArea}
@@ -5137,11 +5132,6 @@
}
@Override
- public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
- ActivityTaskManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
- }
-
- @Override
public boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) {
return ActivityTaskManagerService.this.checkCanCloseSystemDialogs(pid, uid,
packageName);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index f8b4987..90070c8 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -78,6 +78,10 @@
import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
+import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
+import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
@@ -96,9 +100,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.ResourceId;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -136,15 +137,12 @@
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.DumpUtils.Dump;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
-import com.android.server.AttributeCache;
-import com.android.server.wm.animation.ClipRectLRAnimation;
-import com.android.server.wm.animation.ClipRectTBAnimation;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -184,6 +182,8 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
+ private final TransitionAnimation mTransitionAnimation;
+
private @TransitionFlags int mNextAppTransitionFlags = 0;
private final ArrayList<Integer> mNextAppTransitionRequests = new ArrayList<>();
private @TransitionOldType int mLastUsedAppTransition = TRANSIT_OLD_UNSET;
@@ -212,12 +212,6 @@
private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
private boolean mNextAppTransitionOverrideRequested;
- // These are the possible states for the enter/exit activities during a thumbnail transition
- private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
- private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
- private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
- private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
-
private String mNextAppTransitionPackage;
// Used for thumbnail transitions. True if we're scaling up, false if scaling down
private boolean mNextAppTransitionScaleUp;
@@ -283,6 +277,7 @@
mService = service;
mHandler = new Handler(service.mH.getLooper());
mDisplayContent = displayContent;
+ mTransitionAnimation = new TransitionAnimation(context, DEBUG_ANIM, TAG);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.linear_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -555,242 +550,18 @@
/** Returns window animation style ID from {@link LayoutParams} or from system in some cases */
@VisibleForTesting
int getAnimationStyleResId(@NonNull LayoutParams lp) {
- int resId = lp.windowAnimations;
- if (lp.type == LayoutParams.TYPE_APPLICATION_STARTING) {
- // Note that we don't want application to customize starting window animation.
- // Since this window is specific for displaying while app starting,
- // application should not change its animation directly.
- // In this case, it will use system resource to get default animation.
- resId = mDefaultWindowAnimationStyleResId;
- }
- return resId;
- }
-
- private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
- if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
- + (lp != null ? lp.packageName : null)
- + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
- if (lp != null && lp.windowAnimations != 0) {
- // If this is a system resource, don't try to load it from the
- // application resources. It is nice to avoid loading application
- // resources if we can.
- String packageName = lp.packageName != null ? lp.packageName : "android";
- int resId = getAnimationStyleResId(lp);
- if ((resId&0xFF000000) == 0x01000000) {
- packageName = "android";
- }
- if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
- + packageName);
- return AttributeCache.instance().get(packageName, resId,
- com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
- }
- return null;
- }
-
- private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
- if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package="
- + packageName + " resId=0x" + Integer.toHexString(resId));
- if (packageName != null) {
- if ((resId&0xFF000000) == 0x01000000) {
- packageName = "android";
- }
- if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
- + packageName);
- return AttributeCache.instance().get(packageName, resId,
- com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
- }
- return null;
- }
-
- Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
- int resId = Resources.ID_NULL;
- Context context = mContext;
- if (animAttr >= 0) {
- AttributeCache.Entry ent = getCachedAnimations(lp);
- if (ent != null) {
- context = ent.context;
- resId = ent.array.getResourceId(animAttr, 0);
- }
- }
- resId = updateToTranslucentAnimIfNeeded(resId, transit);
- if (ResourceId.isValid(resId)) {
- return loadAnimationSafely(context, resId);
- }
- return null;
- }
-
- private Animation loadAnimationRes(LayoutParams lp, int resId) {
- Context context = mContext;
- if (ResourceId.isValid(resId)) {
- AttributeCache.Entry ent = getCachedAnimations(lp);
- if (ent != null) {
- context = ent.context;
- }
- return loadAnimationSafely(context, resId);
- }
- return null;
- }
-
- private Animation loadAnimationRes(String packageName, int resId) {
- if (ResourceId.isValid(resId)) {
- AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
- if (ent != null) {
- return loadAnimationSafely(ent.context, resId);
- }
- }
- return null;
+ return mTransitionAnimation.getAnimationStyleResId(lp);
}
@VisibleForTesting
+ @Nullable
Animation loadAnimationSafely(Context context, int resId) {
- try {
- return AnimationUtils.loadAnimation(context, resId);
- } catch (NotFoundException e) {
- Slog.w(TAG, "Unable to load animation resource", e);
- return null;
- }
+ return TransitionAnimation.loadAnimationSafely(context, resId, TAG);
}
- private int updateToTranslucentAnimIfNeeded(int anim, @TransitionOldType int transit) {
- if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN
- && anim == R.anim.activity_open_enter) {
- return R.anim.activity_translucent_open_enter;
- }
- if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE
- && anim == R.anim.activity_close_exit) {
- return R.anim.activity_translucent_close_exit;
- }
- return anim;
- }
-
- /**
- * Compute the pivot point for an animation that is scaling from a small
- * rect on screen to a larger rect. The pivot point varies depending on
- * the distance between the inner and outer edges on both sides. This
- * function computes the pivot point for one dimension.
- * @param startPos Offset from left/top edge of outer rectangle to
- * left/top edge of inner rectangle.
- * @param finalScale The scaling factor between the size of the outer
- * and inner rectangles.
- */
- private static float computePivot(int startPos, float finalScale) {
-
- /*
- Theorem of intercepting lines:
-
- + + +-----------------------------------------------+
- | | | |
- | | | |
- | | | |
- | | | |
- x | y | | |
- | | | |
- | | | |
- | | | |
- | | | |
- | + | +--------------------+ |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | +--------------------+ |
- | | |
- | | |
- | | |
- | | |
- | | |
- | | |
- | | |
- | +-----------------------------------------------+
- |
- |
- |
- |
- |
- |
- |
- |
- |
- + ++
- p ++
-
- scale = (x - y) / x
- <=> x = -y / (scale - 1)
- */
- final float denom = finalScale-1;
- if (Math.abs(denom) < .0001f) {
- return startPos;
- }
- return -startPos / denom;
- }
-
- private Animation createScaleUpAnimationLocked(int transit, boolean enter,
- Rect containingFrame) {
- Animation a;
- getDefaultNextAppTransitionStartRect(mTmpRect);
- final int appWidth = containingFrame.width();
- final int appHeight = containingFrame.height();
- if (enter) {
- // Entering app zooms out from the center of the initial rect.
- float scaleW = mTmpRect.width() / (float) appWidth;
- float scaleH = mTmpRect.height() / (float) appHeight;
- Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mTmpRect.left, scaleW),
- computePivot(mTmpRect.top, scaleH));
- scale.setInterpolator(mDecelerateInterpolator);
-
- Animation alpha = new AlphaAnimation(0, 1);
- alpha.setInterpolator(mThumbnailFadeOutInterpolator);
-
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(scale);
- set.addAnimation(alpha);
- set.setDetachWallpaper(true);
- a = set;
- } else if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN
- || transit == TRANSIT_OLD_WALLPAPER_INTRA_CLOSE) {
- // If we are on top of the wallpaper, we need an animation that
- // correctly handles the wallpaper staying static behind all of
- // the animated elements. To do this, will just have the existing
- // element fade out.
- a = new AlphaAnimation(1, 0);
- a.setDetachWallpaper(true);
- } else {
- // For normal animations, the exiting element just holds in place.
- a = new AlphaAnimation(1, 1);
- }
-
- // Pick the desired duration. If this is an inter-activity transition,
- // it is the standard duration for that. Otherwise we use the longer
- // task transition duration.
- final long duration;
- switch (transit) {
- case TRANSIT_OLD_ACTIVITY_OPEN:
- case TRANSIT_OLD_ACTIVITY_CLOSE:
- duration = mConfigShortAnimTime;
- break;
- default:
- duration = DEFAULT_APP_TRANSITION_DURATION;
- break;
- }
- a.setDuration(duration);
- a.setFillAfter(true);
- a.setInterpolator(mDecelerateInterpolator);
- a.initialize(appWidth, appHeight, appWidth, appHeight);
- return a;
+ @Nullable
+ Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
+ return mTransitionAnimation.loadAnimationAttr(lp, animAttr, transit);
}
private void getDefaultNextAppTransitionStartRect(Rect rect) {
@@ -825,27 +596,6 @@
}
/**
- * @return the duration of the last clip reveal animation
- */
- long getLastClipRevealTransitionDuration() {
- return mLastClipRevealTransitionDuration;
- }
-
- /**
- * @return the maximum distance the app surface is traveling of the last clip reveal animation
- */
- int getLastClipRevealMaxTranslation() {
- return mLastClipRevealMaxTranslation;
- }
-
- /**
- * @return true if in the last app transition had a clip reveal animation, false otherwise
- */
- boolean hadClipRevealAnimation() {
- return mLastHadClipReveal;
- }
-
- /**
* Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
* the start rect is outside of the target rect, and there is a lot of movement going on.
*
@@ -867,137 +617,21 @@
(MAX_CLIP_REVEAL_TRANSITION_DURATION - DEFAULT_APP_TRANSITION_DURATION));
}
- private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
- Rect displayFrame) {
- final Animation anim;
- if (enter) {
- final int appWidth = appFrame.width();
- final int appHeight = appFrame.height();
-
- // mTmpRect will contain an area around the launcher icon that was pressed. We will
- // clip reveal from that area in the final area of the app.
- getDefaultNextAppTransitionStartRect(mTmpRect);
-
- float t = 0f;
- if (appHeight > 0) {
- t = (float) mTmpRect.top / displayFrame.height();
- }
- int translationY = mClipRevealTranslationY + (int)(displayFrame.height() / 7f * t);
- int translationX = 0;
- int translationYCorrection = translationY;
- int centerX = mTmpRect.centerX();
- int centerY = mTmpRect.centerY();
- int halfWidth = mTmpRect.width() / 2;
- int halfHeight = mTmpRect.height() / 2;
- int clipStartX = centerX - halfWidth - appFrame.left;
- int clipStartY = centerY - halfHeight - appFrame.top;
- boolean cutOff = false;
-
- // If the starting rectangle is fully or partially outside of the target rectangle, we
- // need to start the clipping at the edge and then achieve the rest with translation
- // and extending the clip rect from that edge.
- if (appFrame.top > centerY - halfHeight) {
- translationY = (centerY - halfHeight) - appFrame.top;
- translationYCorrection = 0;
- clipStartY = 0;
- cutOff = true;
- }
- if (appFrame.left > centerX - halfWidth) {
- translationX = (centerX - halfWidth) - appFrame.left;
- clipStartX = 0;
- cutOff = true;
- }
- if (appFrame.right < centerX + halfWidth) {
- translationX = (centerX + halfWidth) - appFrame.right;
- clipStartX = appWidth - mTmpRect.width();
- cutOff = true;
- }
- final long duration = calculateClipRevealTransitionDuration(cutOff, translationX,
- translationY, displayFrame);
-
- // Clip third of the from size of launch icon, expand to full width/height
- Animation clipAnimLR = new ClipRectLRAnimation(
- clipStartX, clipStartX + mTmpRect.width(), 0, appWidth);
- clipAnimLR.setInterpolator(mClipHorizontalInterpolator);
- clipAnimLR.setDuration((long) (duration / 2.5f));
-
- TranslateAnimation translate = new TranslateAnimation(translationX, 0, translationY, 0);
- translate.setInterpolator(cutOff ? TOUCH_RESPONSE_INTERPOLATOR
- : mLinearOutSlowInInterpolator);
- translate.setDuration(duration);
-
- Animation clipAnimTB = new ClipRectTBAnimation(
- clipStartY, clipStartY + mTmpRect.height(),
- 0, appHeight,
- translationYCorrection, 0,
- mLinearOutSlowInInterpolator);
- clipAnimTB.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- clipAnimTB.setDuration(duration);
-
- // Quick fade-in from icon to app window
- final long alphaDuration = duration / 4;
- AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
- alpha.setDuration(alphaDuration);
- alpha.setInterpolator(mLinearOutSlowInInterpolator);
-
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(clipAnimLR);
- set.addAnimation(clipAnimTB);
- set.addAnimation(translate);
- set.addAnimation(alpha);
- set.setZAdjustment(Animation.ZORDER_TOP);
- set.initialize(appWidth, appHeight, appWidth, appHeight);
- anim = set;
- mLastHadClipReveal = true;
- mLastClipRevealTransitionDuration = duration;
-
- // If the start rect was full inside the target rect (cutOff == false), we don't need
- // to store the translation, because it's only used if cutOff == true.
- mLastClipRevealMaxTranslation = cutOff
- ? Math.max(Math.abs(translationY), Math.abs(translationX)) : 0;
- } else {
- final long duration;
- switch (transit) {
- case TRANSIT_OLD_ACTIVITY_OPEN:
- case TRANSIT_OLD_ACTIVITY_CLOSE:
- duration = mConfigShortAnimTime;
- break;
- default:
- duration = DEFAULT_APP_TRANSITION_DURATION;
- break;
- }
- if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN
- || transit == TRANSIT_OLD_WALLPAPER_INTRA_CLOSE) {
- // If we are on top of the wallpaper, we need an animation that
- // correctly handles the wallpaper staying static behind all of
- // the animated elements. To do this, will just have the existing
- // element fade out.
- anim = new AlphaAnimation(1, 0);
- anim.setDetachWallpaper(true);
- } else {
- // For normal animations, the exiting element just holds in place.
- anim = new AlphaAnimation(1, 1);
- }
- anim.setInterpolator(mDecelerateInterpolator);
- anim.setDuration(duration);
- anim.setFillAfter(true);
- }
- return anim;
- }
-
/**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
- Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
- long duration, Interpolator interpolator) {
- if (duration > 0) {
- a.setDuration(duration);
+ Animation prepareThumbnailAnimationWithDuration(@Nullable Animation a, int appWidth,
+ int appHeight, long duration, Interpolator interpolator) {
+ if (a != null) {
+ if (duration > 0) {
+ a.setDuration(duration);
+ }
+ a.setFillAfter(true);
+ if (interpolator != null) {
+ a.setInterpolator(interpolator);
+ }
+ a.initialize(appWidth, appHeight, appWidth, appHeight);
}
- a.setFillAfter(true);
- if (interpolator != null) {
- a.setInterpolator(interpolator);
- }
- a.initialize(appWidth, appHeight, appWidth, appHeight);
return a;
}
@@ -1069,8 +703,8 @@
}
Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
- final Animation animation = loadAnimationRes(
- "android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
+ final Animation animation =
+ mTransitionAnimation.loadCrossProfileAppThumbnailEnterAnimation();
return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
appRect.height(), 0, null);
}
@@ -1203,145 +837,6 @@
return TOUCH_RESPONSE_INTERPOLATOR;
}
- /**
- * This alternate animation is created when we are doing a thumbnail transition, for the
- * activity that is leaving, and the activity that is entering.
- */
- Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
- int uiMode, int orientation, int transit, Rect containingFrame, Rect contentInsets,
- @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform,
- WindowContainer container) {
- Animation a;
- final int appWidth = containingFrame.width();
- final int appHeight = containingFrame.height();
- getDefaultNextAppTransitionStartRect(mTmpRect);
- final int thumbWidthI = mTmpRect.width();
- final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mTmpRect.height();
- final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
- final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left;
- final int thumbStartY = mTmpRect.top - containingFrame.top;
-
- switch (thumbTransitState) {
- case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
- case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
- final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
- if (freeform && scaleUp) {
- a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
- containingFrame, surfaceInsets, container);
- } else if (freeform) {
- a = createAspectScaledThumbnailExitFreeformAnimationLocked(
- containingFrame, surfaceInsets, container);
- } else {
- AnimationSet set = new AnimationSet(true);
-
- // In portrait, we scale to fit the width
- mTmpFromClipRect.set(containingFrame);
- mTmpToClipRect.set(containingFrame);
-
- // Containing frame is in screen space, but we need the clip rect in the
- // app space.
- mTmpFromClipRect.offsetTo(0, 0);
- mTmpToClipRect.offsetTo(0, 0);
-
- // Exclude insets region from the source clip.
- mTmpFromClipRect.inset(contentInsets);
- mNextAppTransitionInsets.set(contentInsets);
-
- if (shouldScaleDownThumbnailTransition(uiMode, orientation)) {
- // We scale the width and clip to the top/left square
- float scale = thumbWidth /
- (appWidth - contentInsets.left - contentInsets.right);
- if (!mGridLayoutRecentsEnabled) {
- int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
- }
-
- mNextAppTransitionInsets.set(contentInsets);
-
- Animation scaleAnim = new ScaleAnimation(
- scaleUp ? scale : 1, scaleUp ? 1 : scale,
- scaleUp ? scale : 1, scaleUp ? 1 : scale,
- containingFrame.width() / 2f,
- containingFrame.height() / 2f + contentInsets.top);
- final float targetX = (mTmpRect.left - containingFrame.left);
- final float x = containingFrame.width() / 2f
- - containingFrame.width() / 2f * scale;
- final float targetY = (mTmpRect.top - containingFrame.top);
- float y = containingFrame.height() / 2f
- - containingFrame.height() / 2f * scale;
-
- // During transition may require clipping offset from any top stable insets
- // such as the statusbar height when statusbar is hidden
- if (mLowRamRecentsEnabled && contentInsets.top == 0 && scaleUp) {
- mTmpFromClipRect.top += stableInsets.top;
- y += stableInsets.top;
- }
- final float startX = targetX - x;
- final float startY = targetY - y;
- Animation clipAnim = scaleUp
- ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
- : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
- Animation translateAnim = scaleUp
- ? createCurvedMotion(startX, 0, startY - contentInsets.top, 0)
- : createCurvedMotion(0, startX, 0, startY - contentInsets.top);
-
- set.addAnimation(clipAnim);
- set.addAnimation(scaleAnim);
- set.addAnimation(translateAnim);
-
- } else {
- // In landscape, we don't scale at all and only crop
- mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI;
- mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI;
-
- Animation clipAnim = scaleUp
- ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
- : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
- Animation translateAnim = scaleUp
- ? createCurvedMotion(thumbStartX, 0,
- thumbStartY - contentInsets.top, 0)
- : createCurvedMotion(0, thumbStartX, 0,
- thumbStartY - contentInsets.top);
-
- set.addAnimation(clipAnim);
- set.addAnimation(translateAnim);
- }
- a = set;
- a.setZAdjustment(Animation.ZORDER_TOP);
- }
- break;
- }
- case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
- // Previous app window during the scale up
- if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN) {
- // Fade out the source activity if we are animating to a wallpaper
- // activity.
- a = new AlphaAnimation(1, 0);
- } else {
- a = new AlphaAnimation(1, 1);
- }
- break;
- }
- case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
- // Target app window during the scale down
- if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN) {
- // Fade in the destination activity if we are animating from a wallpaper
- // activity.
- a = new AlphaAnimation(0, 1);
- } else {
- a = new AlphaAnimation(1, 1);
- }
- break;
- }
- default:
- throw new RuntimeException("Invalid thumbnail transition state");
- }
-
- return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
- getAspectScaleDuration(), getAspectScaleInterpolator());
- }
-
private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
@Nullable Rect surfaceInsets, WindowContainer container) {
getNextAppTransitionStartRect(container, mTmpRect);
@@ -1408,8 +903,8 @@
float scaleW = appWidth / thumbWidth;
float scaleH = appHeight / thumbHeight;
Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mTmpRect.left, 1 / scaleW),
- computePivot(mTmpRect.top, 1 / scaleH));
+ TransitionAnimation.computePivot(mTmpRect.left, 1 / scaleW),
+ TransitionAnimation.computePivot(mTmpRect.top, 1 / scaleH));
scale.setInterpolator(mDecelerateInterpolator);
Animation alpha = new AlphaAnimation(1, 0);
@@ -1425,125 +920,14 @@
float scaleW = appWidth / thumbWidth;
float scaleH = appHeight / thumbHeight;
a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mTmpRect.left, 1 / scaleW),
- computePivot(mTmpRect.top, 1 / scaleH));
+ TransitionAnimation.computePivot(mTmpRect.left, 1 / scaleW),
+ TransitionAnimation.computePivot(mTmpRect.top, 1 / scaleH));
}
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
}
/**
- * This animation is created when we are doing a thumbnail transition, for the activity that is
- * leaving, and the activity that is entering.
- */
- Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame,
- int transit, WindowContainer container) {
- final int appWidth = containingFrame.width();
- final int appHeight = containingFrame.height();
- final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container);
- Animation a;
- getDefaultNextAppTransitionStartRect(mTmpRect);
- final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
- final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
- final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
-
- switch (thumbTransitState) {
- case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
- // Entering app scales up with the thumbnail
- float scaleW = thumbWidth / appWidth;
- float scaleH = thumbHeight / appHeight;
- a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mTmpRect.left, scaleW),
- computePivot(mTmpRect.top, scaleH));
- break;
- }
- case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
- // Exiting app while the thumbnail is scaling up should fade or stay in place
- if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN) {
- // Fade out while bringing up selected activity. This keeps the
- // current activity from showing through a launching wallpaper
- // activity.
- a = new AlphaAnimation(1, 0);
- } else {
- // noop animation
- a = new AlphaAnimation(1, 1);
- }
- break;
- }
- case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
- // Entering the other app, it should just be visible while we scale the thumbnail
- // down above it
- a = new AlphaAnimation(1, 1);
- break;
- }
- case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
- // Exiting the current app, the app should scale down with the thumbnail
- float scaleW = thumbWidth / appWidth;
- float scaleH = thumbHeight / appHeight;
- Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mTmpRect.left, scaleW),
- computePivot(mTmpRect.top, scaleH));
-
- Animation alpha = new AlphaAnimation(1, 0);
-
- AnimationSet set = new AnimationSet(true);
- set.addAnimation(scale);
- set.addAnimation(alpha);
- set.setZAdjustment(Animation.ZORDER_TOP);
- a = set;
- break;
- }
- default:
- throw new RuntimeException("Invalid thumbnail transition state");
- }
-
- return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
- }
-
- private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) {
- getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
- final int left = mTmpFromClipRect.left;
- final int top = mTmpFromClipRect.top;
- mTmpFromClipRect.offset(-left, -top);
- // TODO: Isn't that strange that we ignore exact position of the containingFrame?
- mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height());
- AnimationSet set = new AnimationSet(true);
- float fromWidth = mTmpFromClipRect.width();
- float toWidth = mTmpToClipRect.width();
- float fromHeight = mTmpFromClipRect.height();
- // While the window might span the whole display, the actual content will be cropped to the
- // system decoration frame, for example when the window is docked. We need to take into
- // account the visible height when constructing the animation.
- float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom;
- int translateAdjustment = 0;
- if (fromWidth <= toWidth && fromHeight <= toHeight) {
- // The final window is larger in both dimensions than current window (e.g. we are
- // maximizing), so we can simply unclip the new window and there will be no disappearing
- // frame.
- set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect));
- } else {
- // The disappearing window has one larger dimension. We need to apply scaling, so the
- // first frame of the entry animation matches the old window.
- set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
- // We might not be going exactly full screen, but instead be aligned under the status
- // bar using cropping. We still need to account for the cropped part, which will also
- // be scaled.
- translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight);
- }
-
- // We animate the translation from the old position of the removed window, to the new
- // position of the added window. The latter might not be full screen, for example docked for
- // docked windows.
- TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
- 0, top - containingFrame.top - translateAdjustment, 0);
- set.addAnimation(translate);
- set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
- set.setZAdjustment(Animation.ZORDER_TOP);
- return set;
- }
-
- /**
* @return true if and only if the first frame of the transition can be skipped, i.e. the first
* frame of the transition doesn't change the visuals on screen, so we can start
* directly with the second one
@@ -1581,6 +965,7 @@
* to the recents thumbnail and hence need to account for the surface being
* bigger.
*/
+ @Nullable
Animation loadAnimation(LayoutParams lp, int transit, boolean enter, int uiMode,
int orientation, Rect frame, Rect displayFrame, Rect insets,
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
@@ -1592,57 +977,61 @@
Animation a;
if (isKeyguardGoingAwayTransitOld(transit) && enter) {
- a = loadKeyguardExitAnimation(transit);
+ a = mTransitionAnimation.loadKeyguardExitAnimation(transit, mNextAppTransitionFlags);
} else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
a = null;
} else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE && !enter) {
- a = loadAnimationRes(lp, com.android.internal.R.anim.wallpaper_open_exit);
+ a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(lp);
} else if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
a = null;
} else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_OPEN
|| transit == TRANSIT_OLD_TASK_OPEN
|| transit == TRANSIT_OLD_TASK_TO_FRONT)) {
- a = loadAnimationRes(lp, enter
- ? com.android.internal.R.anim.voice_activity_open_enter
- : com.android.internal.R.anim.voice_activity_open_exit);
+ a = mTransitionAnimation.loadVoiceActivityOpenAnimation(lp, enter);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a,
appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_CLOSE
|| transit == TRANSIT_OLD_TASK_CLOSE
|| transit == TRANSIT_OLD_TASK_TO_BACK)) {
- a = loadAnimationRes(lp, enter
- ? com.android.internal.R.anim.voice_activity_close_enter
- : com.android.internal.R.anim.voice_activity_close_exit);
+ a = mTransitionAnimation.loadVoiceActivityExitAnimation(lp, enter);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a,
appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else if (transit == TRANSIT_OLD_ACTIVITY_RELAUNCH) {
- a = createRelaunchAnimation(frame, insets);
+ a = mTransitionAnimation.createRelaunchAnimation(frame, insets,
+ mDefaultNextAppTransitionAnimationSpec != null
+ ? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s transit=%s Callers=%s", a,
appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
- a = loadAnimationRes(mNextAppTransitionPackage, enter ?
- mNextAppTransitionEnter : mNextAppTransitionExit);
+ a = mTransitionAnimation.loadAppTransitionAnimation(mNextAppTransitionPackage,
+ enter ? mNextAppTransitionEnter : mNextAppTransitionExit);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s "
+ "isEntrance=%b Callers=%s",
a, appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
- a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
+ a = mTransitionAnimation.loadAppTransitionAnimation(
+ mNextAppTransitionPackage, mNextAppTransitionInPlace);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM_IN_PLACE "
+ "transit=%s Callers=%s",
a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
- a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame);
+ a = mTransitionAnimation.createClipRevealAnimationLocked(
+ transit, enter, frame, displayFrame,
+ mDefaultNextAppTransitionAnimationSpec != null
+ ? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=ANIM_CLIP_REVEAL "
+ "transit=%s Callers=%s",
a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
- a = createScaleUpAnimationLocked(transit, enter, frame);
+ a = mTransitionAnimation.createScaleUpAnimationLocked(transit, enter, frame,
+ mDefaultNextAppTransitionAnimationSpec != null
+ ? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s "
+ "isEntrance=%s Callers=%s",
@@ -1651,8 +1040,11 @@
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
- a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
- frame, transit, container);
+ final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container);
+ a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(
+ getThumbnailTransitionState(enter), frame, transit, thumbnailHeader,
+ mDefaultNextAppTransitionAnimationSpec != null
+ ? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b "
+ "Callers=%s",
@@ -1663,9 +1055,13 @@
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
- a = createAspectScaledThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), uiMode, orientation, transit, frame,
- insets, surfaceInsets, stableInsets, freeform, container);
+ AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
+ container.hashCode());
+ a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(
+ getThumbnailTransitionState(enter), orientation, transit, frame,
+ insets, surfaceInsets, stableInsets, freeform, spec != null ? spec.rect : null,
+ mDefaultNextAppTransitionAnimationSpec != null
+ ? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b "
+ "Callers=%s",
@@ -1674,8 +1070,7 @@
: "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN",
appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS && enter) {
- a = loadAnimationRes("android",
- com.android.internal.R.anim.task_open_enter_cross_profile_apps);
+ a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: "
+ "anim=%s transit=%s isEntrance=true Callers=%s",
@@ -1758,18 +1153,6 @@
return a;
}
- private Animation loadKeyguardExitAnimation(int transit) {
- if ((mNextAppTransitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) != 0) {
- return null;
- }
- final boolean toShade =
- (mNextAppTransitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0;
- final boolean subtle =
- (mNextAppTransitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0;
- return mService.mPolicy.createHiddenByKeyguardExit(
- transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER, toShade, subtle);
- }
-
int getAppRootTaskClipMode() {
return mNextAppTransitionRequests.contains(TRANSIT_RELAUNCH)
|| mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_GOING_AWAY)
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
index 200f207..eec648d 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
@@ -31,7 +31,8 @@
* Note that if the start was allowed due to a mechanism other than tokens (eg. permission),
* this won't be called.
*
- * This will be called holding the WM lock, don't do anything costly here.
+ * This will be called holding the WM and local lock, don't do anything costly or invoke AM/WM
+ * methods here directly.
*/
boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid, String packageName);
@@ -40,7 +41,8 @@
* #ACTION_CLOSE_SYSTEM_DIALOGS}, presumably to start activities, based on the originating
* tokens {@code tokens} currently associated with potential activity starts.
*
- * This will be called holding the AM and WM lock, don't do anything costly here.
+ * This will be called holding the AM and local lock, don't do anything costly or invoke AM/WM
+ * methods here directly.
*/
boolean canCloseSystemDialogs(Collection<IBinder> tokens, int uid);
}
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
new file mode 100644
index 0000000..ab1ed67
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -0,0 +1,264 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.function.IntPredicate;
+
+/**
+ * A per-process controller to decide whether the process can start activity or foreground service
+ * (especially from background). All methods of this class must be thread safe. The caller does not
+ * need to hold WM lock, e.g. lock contention of WM lock shouldn't happen when starting service.
+ */
+class BackgroundLaunchProcessController {
+ private static final String TAG =
+ TAG_WITH_CLASS_NAME ? "BackgroundLaunchProcessController" : TAG_ATM;
+
+ /** It is {@link ActivityTaskManagerService#hasActiveVisibleWindow(int)}. */
+ private final IntPredicate mUidHasActiveVisibleWindowPredicate;
+
+ private final @Nullable BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+
+ /**
+ * A set of tokens that currently contribute to this process being temporarily allowed
+ * to start activities even if it's not in the foreground. The values of this map are optional
+ * (can be null) and are used to trace back the grant to the notification token mechanism.
+ */
+ @GuardedBy("this")
+ private @Nullable ArrayMap<Binder, IBinder> mBackgroundActivityStartTokens;
+
+ /** Set of UIDs of clients currently bound to this process. */
+ @GuardedBy("this")
+ private @Nullable IntArray mBoundClientUids;
+
+ BackgroundLaunchProcessController(@NonNull IntPredicate uidHasActiveVisibleWindowPredicate,
+ @Nullable BackgroundActivityStartCallback callback) {
+ mUidHasActiveVisibleWindowPredicate = uidHasActiveVisibleWindowPredicate;
+ mBackgroundActivityStartCallback = callback;
+ }
+
+ boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
+ boolean appSwitchAllowed, boolean isCheckingForFgsStart, boolean hasVisibleActivities,
+ boolean hasBackgroundActivityStartPrivileges, long lastStopAppSwitchesTime,
+ long lastActivityLaunchTime, long lastActivityFinishTime) {
+ // If app switching is not allowed, we ignore all the start activity grace period
+ // exception so apps cannot start itself in onPause() after pressing home button.
+ if (appSwitchAllowed) {
+ // Allow if any activity in the caller has either started or finished very recently, and
+ // it must be started or finished after last stop app switches time.
+ final long now = SystemClock.uptimeMillis();
+ if (now - lastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
+ || now - lastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
+ // If activity is started and finished before stop app switch time, we should not
+ // let app to be able to start background activity even it's in grace period.
+ if (lastActivityLaunchTime > lastStopAppSwitchesTime
+ || lastActivityFinishTime > lastStopAppSwitchesTime) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "[Process(" + pid
+ + ")] Activity start allowed: within "
+ + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
+ }
+ return true;
+ }
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "[Process(" + pid + ")] Activity start within "
+ + ACTIVITY_BG_START_GRACE_PERIOD_MS
+ + "ms grace period but also within stop app switch window");
+ }
+
+ }
+ }
+ // Allow if the proc is instrumenting with background activity starts privs.
+ if (hasBackgroundActivityStartPrivileges) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "[Process(" + pid
+ + ")] Activity start allowed: process instrumenting with background "
+ + "activity starts privileges");
+ }
+ return true;
+ }
+ // Allow if the caller has an activity in any foreground task.
+ if (appSwitchAllowed && hasVisibleActivities) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "[Process(" + pid
+ + ")] Activity start allowed: process has activity in foreground task");
+ }
+ return true;
+ }
+ // Allow if the caller is bound by a UID that's currently foreground.
+ if (isBoundByForegroundUid()) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "[Process(" + pid
+ + ")] Activity start allowed: process bound by foreground uid");
+ }
+ return true;
+ }
+ // Allow if the flag was explicitly set.
+ if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "[Process(" + pid
+ + ")] Activity start allowed: process allowed by token");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * If there are no tokens, we don't allow *by token*. If there are tokens and
+ * isCheckingForFgsStart is false, we ask the callback if the start is allowed for these tokens,
+ * otherwise if there is no callback we allow.
+ */
+ private boolean isBackgroundStartAllowedByToken(int uid, String packageName,
+ boolean isCheckingForFgsStart) {
+ synchronized (this) {
+ if (mBackgroundActivityStartTokens == null
+ || mBackgroundActivityStartTokens.isEmpty()) {
+ return false;
+ }
+ if (isCheckingForFgsStart) {
+ // BG-FGS-start only checks if there is a token.
+ return true;
+ }
+
+ if (mBackgroundActivityStartCallback == null) {
+ // We have tokens but no callback to decide => allow.
+ return true;
+ }
+ // The callback will decide.
+ return mBackgroundActivityStartCallback.isActivityStartAllowed(
+ mBackgroundActivityStartTokens.values(), uid, packageName);
+ }
+ }
+
+ private boolean isBoundByForegroundUid() {
+ synchronized (this) {
+ if (mBoundClientUids != null) {
+ for (int i = mBoundClientUids.size() - 1; i >= 0; i--) {
+ if (mUidHasActiveVisibleWindowPredicate.test(mBoundClientUids.get(i))) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ void setBoundClientUids(ArraySet<Integer> boundClientUids) {
+ synchronized (this) {
+ if (boundClientUids == null || boundClientUids.isEmpty()) {
+ mBoundClientUids = null;
+ return;
+ }
+ if (mBoundClientUids == null) {
+ mBoundClientUids = new IntArray();
+ } else {
+ mBoundClientUids.clear();
+ }
+ for (int i = boundClientUids.size() - 1; i >= 0; i--) {
+ mBoundClientUids.add(boundClientUids.valueAt(i));
+ }
+ }
+ }
+
+ /**
+ * Allows background activity starts using token {@code entity}. Optionally, you can provide
+ * {@code originatingToken} if you have one such originating token, this is useful for tracing
+ * back the grant in the case of the notification token.
+ *
+ * If {@code entity} is already added, this method will update its {@code originatingToken}.
+ */
+ void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
+ @Nullable IBinder originatingToken) {
+ synchronized (this) {
+ if (mBackgroundActivityStartTokens == null) {
+ mBackgroundActivityStartTokens = new ArrayMap<>();
+ }
+ mBackgroundActivityStartTokens.put(entity, originatingToken);
+ }
+ }
+
+ /**
+ * Removes token {@code entity} that allowed background activity starts added via {@link
+ * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+ */
+ void removeAllowBackgroundActivityStartsToken(Binder entity) {
+ synchronized (this) {
+ if (mBackgroundActivityStartTokens != null) {
+ mBackgroundActivityStartTokens.remove(entity);
+ }
+ }
+ }
+
+ /**
+ * Returns whether this process is allowed to close system dialogs via a background activity
+ * start token that allows the close system dialogs operation (eg. notification).
+ */
+ boolean canCloseSystemDialogsByToken(int uid) {
+ if (mBackgroundActivityStartCallback == null) {
+ return false;
+ }
+ synchronized (this) {
+ if (mBackgroundActivityStartTokens == null
+ || mBackgroundActivityStartTokens.isEmpty()) {
+ return false;
+ }
+ return mBackgroundActivityStartCallback.canCloseSystemDialogs(
+ mBackgroundActivityStartTokens.values(), uid);
+ }
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ synchronized (this) {
+ if (mBackgroundActivityStartTokens != null
+ && !mBackgroundActivityStartTokens.isEmpty()) {
+ pw.print(prefix);
+ pw.println("Background activity start tokens (token: originating token):");
+ for (int i = mBackgroundActivityStartTokens.size() - 1; i >= 0; i--) {
+ pw.print(prefix);
+ pw.print(" - ");
+ pw.print(mBackgroundActivityStartTokens.keyAt(i));
+ pw.print(": ");
+ pw.println(mBackgroundActivityStartTokens.valueAt(i));
+ }
+ }
+ if (mBoundClientUids != null && mBoundClientUids.size() > 0) {
+ pw.print(prefix);
+ pw.print("BoundClientUids:");
+ pw.println(Arrays.toString(mBoundClientUids.toArray()));
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
deleted file mode 100644
index eee27c7..0000000
--- a/services/core/java/com/android/server/wm/BarController.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.annotation.NonNull;
-import android.graphics.Rect;
-
-/**
- * Controls state/behavior specific to a system bar window.
- */
-public class BarController {
- private final int mWindowType;
-
- private final Rect mContentFrame = new Rect();
-
- BarController(int windowType) {
- mWindowType = windowType;
- }
-
- /**
- * Sets the frame within which the bar will display its content.
- *
- * This is used to determine if letterboxes interfere with the display of such content.
- */
- void setContentFrame(Rect frame) {
- mContentFrame.set(frame);
- }
-
- private Rect getContentFrame(@NonNull WindowState win) {
- final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType);
- return rotatedContentFrame != null ? rotatedContentFrame : mContentFrame;
- }
-
- boolean isLightAppearanceAllowed(WindowState win) {
- if (win == null) {
- return true;
- }
- return !win.isLetterboxedOverlappingWith(getContentFrame(win));
- }
-
- boolean isTransparentAllowed(WindowState win) {
- if (win == null) {
- return true;
- }
- return win.isTransparentBarAllowed(getContentFrame(win));
- }
-}
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index dbad8b3..a725dd3 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -24,6 +24,9 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.res.CompatibilityInfo;
@@ -32,6 +35,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
@@ -63,6 +67,58 @@
// Compatibility state: compatibility mode is enabled.
private static final int COMPAT_FLAG_ENABLED = 1<<1;
+ /**
+ * CompatModePackages#DOWNSCALED is the gatekeeper of all per-app buffer downscaling
+ * changes. Disabling this change will prevent the following scaling factors from working:
+ * CompatModePackages#DOWNSCALE_87_5
+ * CompatModePackages#DOWNSCALE_75
+ * CompatModePackages#DOWNSCALE_62_5
+ * CompatModePackages#DOWNSCALE_50
+ *
+ * If CompatModePackages#DOWNSCALED is enabled for an app package, then the app will be forcibly
+ * resized to the highest enabled scaling factor e.g. 87.5% if both 87.5% and 75% were
+ * enabled.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALED = 168419799L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_87_5 for a package will force the app to assume it's
+ * running on a display with 87.5% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALE_87_5 = 176926753L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_75 for a package will force the app to assume it's
+ * running on a display with 75% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALE_75 = 176926829L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_62_5 for a package will force the app to assume it's
+ * running on a display with 62.5% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALE_62_5 = 176926771L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_50 for a package will force the app to assume it's
+ * running on a display with 50% vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALE_50 = 176926741L;
+
private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
private static final int MSG_WRITE = 300;
@@ -191,11 +247,39 @@
mHandler.sendMessageDelayed(msg, 10000);
}
+ float getCompatScale(String packageName, int uid) {
+ if (!CompatChanges.isChangeEnabled(
+ DOWNSCALED, packageName, UserHandle.getUserHandleForUid(uid))) {
+ return 1f;
+ }
+ if (CompatChanges.isChangeEnabled(
+ DOWNSCALE_87_5, packageName, UserHandle.getUserHandleForUid(uid))) {
+ // 8/7 == (1 / 0.875) ~= 1.14285714286
+ return 8f / 7f;
+ }
+ if (CompatChanges.isChangeEnabled(
+ DOWNSCALE_75, packageName, UserHandle.getUserHandleForUid(uid))) {
+ // 4/3 == (1 / 0.75) ~= 1.333333333
+ return 4f / 3f;
+ }
+ if (CompatChanges.isChangeEnabled(
+ DOWNSCALE_62_5, packageName, UserHandle.getUserHandleForUid(uid))) {
+ // (1 / 0.625) == 1.6
+ return 1.6f;
+ }
+ if (CompatChanges.isChangeEnabled(
+ DOWNSCALE_50, packageName, UserHandle.getUserHandleForUid(uid))) {
+ return 2f;
+ }
+ return 1f;
+ }
+
public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
final Configuration globalConfig = mService.getGlobalConfiguration();
+ final float requestedScale = getCompatScale(ai.packageName, ai.uid);
CompatibilityInfo ci = new CompatibilityInfo(ai, globalConfig.screenLayout,
globalConfig.smallestScreenWidthDp,
- (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
+ (getPackageFlags(ai.packageName) & COMPAT_FLAG_ENABLED) != 0, requestedScale);
//Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
return ci;
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 15483cb..f075d85 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -462,6 +462,23 @@
}
@Override
+ void resolveOverrideConfiguration(Configuration newParentConfiguration) {
+ super.resolveOverrideConfiguration(newParentConfiguration);
+ final Configuration resolvedConfig = getResolvedOverrideConfiguration();
+ final Rect overrideBounds = resolvedConfig.windowConfiguration.getBounds();
+ final Rect overrideAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
+ final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+
+ // If there is no override of appBounds, restrict appBounds to the override bounds.
+ if (!overrideBounds.isEmpty() && (overrideAppBounds == null || overrideAppBounds.isEmpty())
+ && parentAppBounds != null && !parentAppBounds.isEmpty()) {
+ final Rect appBounds = new Rect(overrideBounds);
+ appBounds.intersect(parentAppBounds);
+ resolvedConfig.windowConfiguration.setAppBounds(appBounds);
+ }
+ }
+
+ @Override
boolean isOrganized() {
return mOrganizer != null;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0aaa1a1..3ab7952 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -204,6 +204,7 @@
import android.view.InsetsState.InternalInsetsType;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationDefinition;
+import android.view.RoundedCorners;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -337,6 +338,10 @@
= new RotationCache<>(this::calculateDisplayCutoutForRotationUncached);
boolean mIgnoreDisplayCutout;
+ RoundedCorners mInitialRoundedCorners;
+ private final RotationCache<RoundedCorners, RoundedCorners> mRoundedCornerCache =
+ new RotationCache<>(this::calculateRoundedCornersForRotationUncached);
+
/**
* Overridden display size. Initialized with {@link #mInitialDisplayWidth}
* and {@link #mInitialDisplayHeight}, but can be set via shell command "adb shell wm size".
@@ -670,8 +675,9 @@
// Used in updating override configurations
private final Configuration mTempConfig = new Configuration();
- // Used in performing layout
- private boolean mTmpWindowsBehindIme;
+ // 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
@@ -770,17 +776,11 @@
+ " parentHidden=" + w.isParentWindowHidden());
}
- // Sets mBehindIme for each window. Windows behind IME can get IME insets.
- if (w.mBehindIme != mTmpWindowsBehindIme) {
- w.mBehindIme = mTmpWindowsBehindIme;
- if (getInsetsStateController().getRawInsetsState().getSourceOrDefaultVisibility(
- ITYPE_IME)) {
- // If IME is invisible, behind IME or not doesn't make the insets different.
- mWinInsetsChanged.add(w);
- }
- }
- if (w == mInputMethodWindow) {
- mTmpWindowsBehindIme = true;
+ // 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
@@ -816,8 +816,16 @@
+ " 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
@@ -980,7 +988,8 @@
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mInsetsStateController = new InsetsStateController(this);
mDisplayFrames = new DisplayFrames(mDisplayId, mInsetsStateController.getRawInsetsState(),
- mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
+ mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation),
+ calculateRoundedCornersForRotation(mDisplayInfo.rotation));
initializeDisplayBaseInfo();
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
@@ -1693,8 +1702,9 @@
mTmpConfiguration.unset();
final DisplayInfo info = computeScreenConfiguration(mTmpConfiguration, rotation);
final WmDisplayCutout cutout = calculateDisplayCutoutForRotation(rotation);
+ final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
final DisplayFrames displayFrames = new DisplayFrames(mDisplayId, new InsetsState(), info,
- cutout);
+ cutout, roundedCorners);
token.applyFixedRotationTransform(info, displayFrames, mTmpConfiguration);
}
@@ -1955,6 +1965,23 @@
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
+ RoundedCorners calculateRoundedCornersForRotation(int rotation) {
+ return mRoundedCornerCache.getOrCompute(mInitialRoundedCorners, rotation);
+ }
+
+ private RoundedCorners calculateRoundedCornersForRotationUncached(
+ RoundedCorners roundedCorners, int rotation) {
+ if (roundedCorners == null || roundedCorners == RoundedCorners.NO_ROUNDED_CORNERS) {
+ return RoundedCorners.NO_ROUNDED_CORNERS;
+ }
+
+ if (rotation == ROTATION_0) {
+ return roundedCorners;
+ }
+
+ return roundedCorners.rotate(rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+ }
+
/**
* Compute display info and configuration according to the given rotation without changing
* current display.
@@ -2482,7 +2509,8 @@
void onDisplayInfoChanged() {
final DisplayInfo info = mDisplayInfo;
- mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation));
+ mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation),
+ calculateRoundedCornersForRotation(info.rotation));
mInputMonitor.layoutInputConsumers(info.logicalWidth, info.logicalHeight);
mDisplayPolicy.onDisplayInfoChanged(info);
}
@@ -2515,6 +2543,7 @@
mInitialDisplayHeight = mDisplayInfo.logicalHeight;
mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
mInitialDisplayCutout = mDisplayInfo.displayCutout;
+ mInitialRoundedCorners = mDisplayInfo.roundedCorners;
}
/**
@@ -2532,11 +2561,13 @@
final DisplayCutout newCutout = mIgnoreDisplayCutout
? DisplayCutout.NO_CUTOUT : mDisplayInfo.displayCutout;
final String newUniqueId = mDisplayInfo.uniqueId;
+ final RoundedCorners newRoundedCorners = mDisplayInfo.roundedCorners;
final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth
|| mInitialDisplayHeight != newHeight
|| mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi
- || !Objects.equals(mInitialDisplayCutout, newCutout);
+ || !Objects.equals(mInitialDisplayCutout, newCutout)
+ || !Objects.equals(mInitialRoundedCorners, newRoundedCorners);
final boolean physicalDisplayChanged = !newUniqueId.equals(mCurrentUniqueDisplayId);
if (displayMetricsChanged || physicalDisplayChanged) {
@@ -2555,6 +2586,7 @@
mInitialDisplayHeight = newHeight;
mInitialDisplayDensity = newDensity;
mInitialDisplayCutout = newCutout;
+ mInitialRoundedCorners = newRoundedCorners;
mCurrentUniqueDisplayId = newUniqueId;
reconfigureDisplayLocked();
}
@@ -4192,12 +4224,14 @@
mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
- mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
- mLastHasContent,
- mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
- mTmpApplySurfaceChangesTransactionState.preferredModeId,
- mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
- true /* inTraversal, must call performTraversalInTrans... below */);
+ if (!mWmService.mDisplayFrozen) {
+ mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
+ mLastHasContent,
+ mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
+ mTmpApplySurfaceChangesTransactionState.preferredModeId,
+ mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
+ true /* inTraversal, must call performTraversalInTrans... below */);
+ }
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
if (wallpaperVisible != mLastWallpaperVisible) {
@@ -4281,14 +4315,20 @@
+ " 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;
mTmpInitial = initial;
- // Used to indicate that we have processed the IME window.
- mTmpWindowsBehindIme = false;
// First perform layout of any root windows (not attached to another window).
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
@@ -5483,14 +5523,16 @@
/** Checks whether the given activity is in size compatibility mode and notifies the change. */
void handleActivitySizeCompatModeIfNeeded(ActivityRecord r) {
- if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+ final Task organizedTask = r.getOrganizedTask();
+ if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
+ || organizedTask == null) {
// The callback is only interested in the foreground changes of fullscreen activity.
return;
}
+ // TODO(b/178327644) Update for per Task size compat
if (!r.inSizeCompatMode()) {
if (mLastCompatModeActivity != null) {
- mAtmService.getTaskChangeNotificationController()
- .notifySizeCompatModeActivityChanged(mDisplayId, null /* activityToken */);
+ organizedTask.onSizeCompatActivityChanged();
}
mLastCompatModeActivity = null;
return;
@@ -5499,8 +5541,7 @@
return;
}
mLastCompatModeActivity = r;
- mAtmService.getTaskChangeNotificationController()
- .notifySizeCompatModeActivityChanged(mDisplayId, r.appToken);
+ organizedTask.onSizeCompatActivityChanged();
}
boolean isUidPresent(int uid) {
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 43c1435..e4230a2 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -21,12 +21,12 @@
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;
import android.view.DisplayInfo;
import android.view.InsetsState;
+import android.view.RoundedCorners;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -47,9 +47,6 @@
*/
public final Rect mUnrestricted = new Rect();
- /** The display cutout used for layout (after rotation) */
- @NonNull public WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
-
/**
* During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
*/
@@ -61,26 +58,37 @@
public int mRotation;
public DisplayFrames(int displayId, InsetsState insetsState, DisplayInfo info,
- WmDisplayCutout displayCutout) {
+ WmDisplayCutout displayCutout, RoundedCorners roundedCorners) {
mDisplayId = displayId;
mInsetsState = insetsState;
- onDisplayInfoUpdated(info, displayCutout);
+ onDisplayInfoUpdated(info, displayCutout, roundedCorners);
}
- public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout) {
+ /**
+ * Update {@link DisplayFrames} when {@link DisplayInfo} is updated.
+ *
+ * @param info the updated {@link DisplayInfo}.
+ * @param displayCutout the updated {@link DisplayCutout}.
+ * @param roundedCorners the updated {@link RoundedCorners}.
+ */
+ public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout,
+ RoundedCorners roundedCorners) {
mDisplayWidth = info.logicalWidth;
mDisplayHeight = info.logicalHeight;
mRotation = info.rotation;
- mDisplayCutout = displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
+ final WmDisplayCutout wmDisplayCutout =
+ displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
final InsetsState state = mInsetsState;
final Rect unrestricted = mUnrestricted;
final Rect safe = mDisplayCutoutSafe;
- final DisplayCutout cutout = mDisplayCutout.getDisplayCutout();
+ final DisplayCutout cutout = wmDisplayCutout.getDisplayCutout();
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);
if (!cutout.isEmpty()) {
if (cutout.getSafeInsetLeft() > 0) {
safe.left = unrestricted.left + cutout.getSafeInsetLeft();
@@ -118,12 +126,5 @@
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
+ " r=" + mRotation);
- final String myPrefix = prefix + " ";
- dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
- pw.println(myPrefix + "mDisplayCutout=" + mDisplayCutout);
- }
-
- private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
- pw.print(prefix + name + "="); frame.printShortString(pw); pw.println();
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a7db9d6..02e281f5 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -37,11 +37,14 @@
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.ITYPE_TOP_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
+import static android.view.ViewRootImpl.computeWindowBounds;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -84,6 +87,7 @@
import static android.view.WindowManagerPolicyConstants.ALT_BAR_UNKNOWN;
import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
@@ -125,7 +129,6 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.util.ArraySet;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -139,8 +142,6 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewDebug;
-import android.view.WindowInsets.Side;
-import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
@@ -165,7 +166,6 @@
import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
-import com.android.server.policy.WindowOrientationListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
@@ -303,8 +303,7 @@
private boolean mLastImmersiveMode;
- private final BarController mStatusBarController;
- private final BarController mNavigationBarController;
+ private final SparseArray<Rect> mBarContentFrames = new SparseArray<>();
// The windows we were told about in focusChanged.
private WindowState mFocusedWindow;
@@ -430,8 +429,8 @@
final int displayId = displayContent.getDisplayId();
- mStatusBarController = new BarController(TYPE_STATUS_BAR);
- mNavigationBarController = new BarController(TYPE_NAVIGATION_BAR);
+ mBarContentFrames.put(TYPE_STATUS_BAR, new Rect());
+ mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect());
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
@@ -1251,11 +1250,6 @@
displayFrames.mDisplayCutoutSafe.top);
}
- @VisibleForTesting
- BarController getStatusBarController() {
- return mStatusBarController;
- }
-
WindowState getStatusBar() {
return mStatusBar != null ? mStatusBar : mStatusBarAlt;
}
@@ -1399,29 +1393,15 @@
*
* @param attrs The LayoutParams of the window.
* @param windowToken The token of the window.
- * @param outFrame The frame of the window.
* @param outInsetsState The insets state of this display from the client's perspective.
* @param localClient Whether the client is from the our process.
* @return Whether to always consume the system bars.
* See {@link #areSystemBarsForcedShownLw(WindowState)}.
*/
- boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, Rect outFrame,
- InsetsState outInsetsState, boolean localClient) {
- final boolean isFixedRotationTransforming =
- windowToken != null && windowToken.isFixedRotationTransforming();
- final ActivityRecord activity = windowToken != null ? windowToken.asActivityRecord() : null;
- final Task task = activity != null ? activity.getTask() : null;
- final Rect taskBounds = isFixedRotationTransforming
- // Use token (activity) bounds if it is rotated because its task is not rotated.
- ? windowToken.getBounds()
- : (task != null ? task.getBounds() : null);
+ boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, InsetsState outInsetsState,
+ boolean localClient) {
final InsetsState state =
mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
- computeWindowBounds(attrs, state, windowToken, outFrame);
- if (taskBounds != null) {
- outFrame.intersect(taskBounds);
- }
-
final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken);
outInsetsState.set(state, inSizeCompatMode || localClient);
if (inSizeCompatMode) {
@@ -1471,7 +1451,7 @@
mSystemGestures.screenHeight = info.logicalHeight;
}
- private void layoutStatusBar(DisplayFrames displayFrames, Rect simulatedContentFrame) {
+ private void layoutStatusBar(DisplayFrames displayFrames, Rect contentFrame) {
// decide where the status bar goes ahead of time
if (mStatusBar == null) {
return;
@@ -1498,21 +1478,16 @@
statusBarBottom);
}
- // Tell the bar controller where the collapsed status bar content is.
sTmpRect.set(windowFrames.mFrame);
sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
sTmpRect.top = windowFrames.mFrame.top; // Ignore top display cutout inset
sTmpRect.bottom = statusBarBottom; // Use collapsed status bar size
- if (simulatedContentFrame != null) {
- simulatedContentFrame.set(sTmpRect);
- } else {
- mStatusBarController.setContentFrame(sTmpRect);
- }
+ contentFrame.set(sTmpRect);
}
- private void layoutNavigationBar(DisplayFrames displayFrames, Rect simulatedContentFrame) {
+ private int layoutNavigationBar(DisplayFrames displayFrames, Rect contentFrame) {
if (mNavigationBar == null) {
- return;
+ return NAV_BAR_INVALID;
}
final int uiMode = mDisplayContent.getConfiguration().uiMode;
@@ -1550,17 +1525,12 @@
windowFrames.setFrames(navigationFrame /* parentFrame */,
navigationFrame /* displayFrame */);
mNavigationBar.computeFrameAndUpdateSourceFrame();
- final Rect contentFrame = sTmpRect;
- contentFrame.set(windowFrames.mFrame);
- contentFrame.intersect(displayFrames.mDisplayCutoutSafe);
- if (simulatedContentFrame != null) {
- simulatedContentFrame.set(contentFrame);
- } else {
- mNavigationBarPosition = navBarPosition;
- mNavigationBarController.setContentFrame(contentFrame);
- }
+ sTmpRect.set(windowFrames.mFrame);
+ sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+ contentFrame.set(sTmpRect);
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
+ return navBarPosition;
}
private boolean canReceiveInput(WindowState win) {
@@ -1572,28 +1542,6 @@
return !notFocusableForIm;
}
- private void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
- @Nullable WindowToken windowToken, Rect outBounds) {
- final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
- final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
- final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
- final Rect df = windowToken != null ? windowToken.getBounds() : state.getDisplayFrame();
- Insets insets = Insets.of(0, 0, 0, 0);
- for (int i = types.size() - 1; i >= 0; i--) {
- final InsetsSource source = state.peekSource(types.valueAt(i));
- if (source == null) {
- continue;
- }
- insets = Insets.max(insets, source.calculateInsets(
- df, attrs.isFitInsetsIgnoringVisibility()));
- }
- final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
- final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
- final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
- final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
- outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom);
- }
-
/**
* Called for each window attached to the window manager as layout is proceeding. The
* implementation of this function must take care of setting the window's frame, either here or
@@ -1607,11 +1555,12 @@
*/
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
if (win == mNavigationBar) {
- layoutNavigationBar(displayFrames, null /* simulatedContentFrame */);
+ mNavigationBarPosition = layoutNavigationBar(displayFrames,
+ mBarContentFrames.get(TYPE_NAVIGATION_BAR));
return;
}
if ((win == mStatusBar && !canReceiveInput(win))) {
- layoutStatusBar(displayFrames, null /* simulatedContentFrame */);
+ layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
return;
}
final WindowManager.LayoutParams attrs = win.getAttrs();
@@ -1633,7 +1582,7 @@
final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
final InsetsState state = win.getInsetsState();
- computeWindowBounds(attrs, state, win.mToken, df);
+ computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
if (attached == null) {
pf.set(df);
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
@@ -2620,7 +2569,7 @@
// Otherwise if it's dimming, clear the light flag.
appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
}
- if (!mStatusBarController.isLightAppearanceAllowed(statusColorWin)) {
+ if (!isLightBarAllowed(statusColorWin, TYPE_STATUS_BAR)) {
appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
}
}
@@ -2683,7 +2632,7 @@
// Clear the light flag for dimming window.
appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
}
- if (!mNavigationBarController.isLightAppearanceAllowed(navColorWin)) {
+ if (!isLightBarAllowed(navColorWin, TYPE_NAVIGATION_BAR)) {
appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
}
}
@@ -2693,34 +2642,17 @@
private int updateSystemBarsLw(WindowState win, int disableFlags) {
final boolean dockedRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
.isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final boolean freeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
- .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
final boolean resizing = mDisplayContent.getDockedDividerController().isResizing();
// We need to force system bars when the docked root task is visible, when the freeform
// root task is focused but also when we are resizing for the transitions when docked
// root task visibility changes.
mForceShowSystemBars = dockedRootTaskVisible || win.inFreeformWindowingMode() || resizing;
- final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing();
-
- final boolean fullscreenDrawsStatusBarBackground =
- drawsStatusBarBackground(mTopFullscreenOpaqueWindowState);
- final boolean dockedDrawsStatusBarBackground =
- drawsStatusBarBackground(mTopDockedOpaqueWindowState);
- final boolean fullscreenDrawsNavBarBackground =
- drawsNavigationBarBackground(mTopFullscreenOpaqueWindowState);
- final boolean dockedDrawsNavigationBarBackground =
- drawsNavigationBarBackground(mTopDockedOpaqueWindowState);
int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
- if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
- appearance &= ~APPEARANCE_OPAQUE_STATUS_BARS;
- }
-
- appearance = configureNavBarOpacity(appearance, dockedRootTaskVisible,
- freeformRootTaskVisible, resizing, fullscreenDrawsNavBarBackground,
- dockedDrawsNavigationBarBackground);
+ appearance = configureStatusBarOpacity(appearance);
+ appearance = configureNavBarOpacity(appearance, dockedRootTaskVisible, resizing);
final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
final long now = SystemClock.uptimeMillis();
@@ -2754,10 +2686,36 @@
return appearance;
}
- private boolean drawsBarBackground(WindowState win, BarController controller) {
- if (!controller.isTransparentAllowed(win)) {
- return false;
+ private boolean isLightBarAllowed(WindowState win, int windowType) {
+ if (win == null) {
+ return true;
}
+ return !win.isLetterboxedOverlappingWith(getBarContentFrameForWindow(win, windowType));
+ }
+
+ private Rect getBarContentFrameForWindow(WindowState win, int windowType) {
+ final Rect rotatedBarFrame = win.mToken.getFixedRotationBarContentFrame(windowType);
+ return rotatedBarFrame != null ? rotatedBarFrame : mBarContentFrames.get(windowType);
+ }
+
+ /**
+ * @return {@code true} if bar is allowed to be fully transparent when given window is show.
+ *
+ * <p>Prevents showing a transparent bar over a letterboxed activity which can make
+ * notification icons or navigation buttons unreadable due to contrast between letterbox
+ * background and an activity. For instance, this happens when letterbox background is solid
+ * black while activity is white. To resolve this, only semi-transparent bars are allowed to
+ * be drawn over letterboxed activity.
+ */
+ @VisibleForTesting
+ boolean isFullyTransparentAllowed(WindowState win, int windowType) {
+ if (win == null) {
+ return true;
+ }
+ return win.isFullyTransparentBarAllowed(getBarContentFrameForWindow(win, windowType));
+ }
+
+ private boolean drawsBarBackground(WindowState win) {
if (win == null) {
return true;
}
@@ -2770,12 +2728,23 @@
return forceDrawsSystemBars || drawsSystemBars;
}
- private boolean drawsStatusBarBackground(WindowState win) {
- return drawsBarBackground(win, mStatusBarController);
- }
+ /** @return the current visibility flags with the status bar opacity related flags toggled. */
+ private int configureStatusBarOpacity(int appearance) {
+ final boolean fullscreenDrawsBackground =
+ drawsBarBackground(mTopFullscreenOpaqueWindowState);
+ final boolean dockedDrawsBackground =
+ drawsBarBackground(mTopDockedOpaqueWindowState);
- private boolean drawsNavigationBarBackground(WindowState win) {
- return drawsBarBackground(win, mNavigationBarController);
+ if (fullscreenDrawsBackground && dockedDrawsBackground) {
+ appearance &= ~APPEARANCE_OPAQUE_STATUS_BARS;
+ }
+
+ if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_STATUS_BAR)
+ || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_STATUS_BAR)) {
+ appearance |= APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+ }
+
+ return appearance;
}
/**
@@ -2783,10 +2752,16 @@
* on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
*/
private int configureNavBarOpacity(int appearance, boolean dockedRootTaskVisible,
- boolean freeformRootTaskVisible, boolean isDockedDividerResizing,
- boolean fullscreenDrawsBackground, boolean dockedDrawsNavigationBarBackground) {
+ boolean isDockedDividerResizing) {
+ final boolean freeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
+ .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
+ final boolean fullscreenDrawsBackground =
+ drawsBarBackground(mTopFullscreenOpaqueWindowState);
+ final boolean dockedDrawsBackground =
+ drawsBarBackground(mTopDockedOpaqueWindowState);
+
if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) {
- if (fullscreenDrawsBackground && dockedDrawsNavigationBarBackground) {
+ if (fullscreenDrawsBackground && dockedDrawsBackground) {
appearance = clearNavBarOpaqueFlag(appearance);
} else if (dockedRootTaskVisible) {
appearance = setNavBarOpaqueFlag(appearance);
@@ -2811,6 +2786,11 @@
}
}
+ if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_NAVIGATION_BAR)
+ || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_NAVIGATION_BAR)) {
+ appearance |= APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
+ }
+
return appearance;
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6a86aee..48e4df7 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -69,7 +69,6 @@
import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.policy.WindowOrientationListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
@@ -253,7 +252,7 @@
if (isDefaultDisplay) {
final Handler uiHandler = UiThread.getHandler();
- mOrientationListener = new OrientationListener(mContext, uiHandler);
+ mOrientationListener = new OrientationListener(mContext, uiHandler, mService);
mOrientationListener.setCurrentRotation(mRotation);
mSettingsObserver = new SettingsObserver(uiHandler);
mSettingsObserver.observe();
@@ -1474,8 +1473,8 @@
final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
boolean mEnabled;
- OrientationListener(Context context, Handler handler) {
- super(context, handler);
+ OrientationListener(Context context, Handler handler, WindowManagerService service) {
+ super(context, handler, service);
}
private class UpdateRunnable implements Runnable {
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1692df6..c6c7fe0 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -151,6 +151,7 @@
// animate-out as new one animates-in.
mWin.cancelAnimation();
mWin.mPendingPositionChanged = null;
+ mWin.mProvidedInsetsSources.remove(mSource.getType());
}
ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
mWin = win;
@@ -160,11 +161,14 @@
setServerVisible(false);
mSource.setFrame(new Rect());
mSource.setVisibleFrame(null);
- } else if (mControllable) {
- mWin.setControllableInsetProvider(this);
- if (mPendingControlTarget != null) {
- updateControlForTarget(mPendingControlTarget, true /* force */);
- mPendingControlTarget = null;
+ } else {
+ mWin.mProvidedInsetsSources.put(mSource.getType(), mSource);
+ if (mControllable) {
+ mWin.setControllableInsetProvider(this);
+ if (mPendingControlTarget != null) {
+ updateControlForTarget(mPendingControlTarget, true /* force */);
+ mPendingControlTarget = null;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 267f677..580d328 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
@@ -104,6 +105,8 @@
* visible to the target. e.g., the source which represents the target window itself, and the
* IME source when the target is above IME. We also need to exclude certain types of insets
* source for client within specific windowing modes.
+ * This is to get the insets for a window layout on the screen. If the window is not there, use
+ * the {@link #getInsetsForWindowMetrics} to get insets instead.
*
* @param target The window associate with the perspective.
* @return The state stripped of the necessary information.
@@ -117,8 +120,8 @@
final @InternalInsetsType int type = provider != null
? provider.getSource().getType() : ITYPE_INVALID;
return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
- isAboveIme(target),
- target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : mState);
+ target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() :
+ target.mAboveInsetsState);
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -133,19 +136,7 @@
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
- return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token), mState);
- }
-
- private boolean isAboveIme(WindowContainer target) {
- final WindowState imeWindow = mDisplayContent.mInputMethodWindow;
- if (target == null || imeWindow == null) {
- return false;
- }
- if (target instanceof WindowState) {
- final WindowState win = (WindowState) target;
- return win.needsRelativeLayeringToIme() || !win.mBehindIme;
- }
- return false;
+ return getInsetsForTarget(type, windowingMode, alwaysOnTop, mState);
}
private static @InternalInsetsType
@@ -181,10 +172,12 @@
* @see #getInsetsForWindowMetrics
*/
private InsetsState getInsetsForTarget(@InternalInsetsType int type,
- @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme,
- @NonNull InsetsState state) {
+ @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) {
+ boolean stateCopied = false;
+
if (type != ITYPE_INVALID) {
state = new InsetsState(state);
+ stateCopied = true;
state.removeSource(type);
// Navigation bar doesn't get influenced by anything else
@@ -219,20 +212,15 @@
if (WindowConfiguration.isFloating(windowingMode)
|| (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
- state = new InsetsState(state);
+ if (!stateCopied) {
+ state = new InsetsState(state);
+ stateCopied = true;
+ }
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_NAVIGATION_BAR);
state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
- }
-
- if (aboveIme) {
- InsetsSource imeSource = state.peekSource(ITYPE_IME);
- if (imeSource != null && imeSource.isVisible()) {
- imeSource = new InsetsSource(imeSource);
- imeSource.setVisible(false);
- imeSource.setFrame(0, 0, 0, 0);
- state = new InsetsState(state);
- state.addSource(imeSource);
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ state.removeSource(ITYPE_IME);
}
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 02a43b7..2274a4a 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -19,6 +19,7 @@
import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.SurfaceControl.HIDDEN;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
@@ -45,6 +46,8 @@
private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
private final Supplier<Boolean> mAreCornersRounded;
+ private final Supplier<Color> mColorSupplier;
+
private final Rect mOuter = new Rect();
private final Rect mInner = new Rect();
private final LetterboxSurface mTop = new LetterboxSurface("top");
@@ -64,10 +67,12 @@
*/
public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- Supplier<Boolean> areCornersRounded) {
+ Supplier<Boolean> areCornersRounded,
+ Supplier<Color> colorSupplier) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
mAreCornersRounded = areCornersRounded;
+ mColorSupplier = colorSupplier;
}
/**
@@ -268,6 +273,7 @@
private final String mType;
private SurfaceControl mSurface;
+ private Color mColor;
private final Rect mSurfaceFrameRelative = new Rect();
private final Rect mLayoutFrameGlobal = new Rect();
@@ -292,9 +298,8 @@
.setColorLayer()
.setCallsite("LetterboxSurface.createSurface")
.build();
- t.setLayer(mSurface, -1)
- .setColor(mSurface, new float[]{0, 0, 0})
- .setColorSpaceAgnostic(mSurface, true);
+
+ t.setLayer(mSurface, -1).setColorSpaceAgnostic(mSurface, true);
}
void attachInput(WindowState win) {
@@ -344,6 +349,14 @@
if (mSurface == null) {
createSurface(t);
}
+
+ mColor = mColorSupplier.get();
+ final float[] rgbTmpFloat = new float[3];
+ rgbTmpFloat[0] = mColor.red();
+ rgbTmpFloat[1] = mColor.green();
+ rgbTmpFloat[2] = mColor.blue();
+ t.setColor(mSurface, rgbTmpFloat);
+
t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
mSurfaceFrameRelative.height());
@@ -358,7 +371,8 @@
}
public boolean needsApplySurfaceChanges() {
- return !mSurfaceFrameRelative.equals(mLayoutFrameRelative);
+ return !mSurfaceFrameRelative.equals(mLayoutFrameRelative)
+ || mColorSupplier.get() != mColor;
}
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e1fdefd..e02cce4 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -421,7 +421,10 @@
if (skipAnimation(task)) {
continue;
}
- addAnimation(task, !recentTaskIds.get(task.mTaskId));
+ addAnimation(task, !recentTaskIds.get(task.mTaskId), false /* hidden */,
+ (type, anim) -> task.forAllWindows(win -> {
+ win.onAnimationFinished(type, anim);
+ }, true /* traverseTopToBottom */));
}
// Skip the animation if there is nothing to animate
diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ScreenshotHashController.java
similarity index 79%
rename from services/core/java/com/android/server/wm/ImpressionAttestationController.java
rename to services/core/java/com/android/server/wm/ScreenshotHashController.java
index bad6c80..03f4e28 100644
--- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java
+++ b/services/core/java/com/android/server/wm/ScreenshotHashController.java
@@ -16,7 +16,9 @@
package com.android.server.wm;
-import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.service.screenshot.ScreenshotHasherService.EXTRA_SCREENSHOT_HASH;
+import static android.service.screenshot.ScreenshotHasherService.EXTRA_VERIFICATION_STATUS;
+import static android.service.screenshot.ScreenshotHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -44,9 +46,9 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.service.attestation.IImpressionAttestationService;
-import android.service.attestation.ImpressionAttestationService;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.IScreenshotHasherService;
+import android.service.screenshot.ScreenshotHash;
+import android.service.screenshot.ScreenshotHasherService;
import android.util.Slog;
import android.view.MagnificationSpec;
@@ -59,30 +61,29 @@
import java.util.function.BiConsumer;
/**
- * Handles requests into {@link ImpressionAttestationService}
+ * Handles requests into {@link android.service.screenshot.ScreenshotHasherService}
*
* Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are
* blocking calls into another service.
*/
-public class ImpressionAttestationController {
- private static final String TAG =
- TAG_WITH_CLASS_NAME ? "ImpressionAttestationController" : TAG_WM;
+public class ScreenshotHashController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenshotHashController" : TAG_WM;
private static final boolean DEBUG = false;
private final Object mServiceConnectionLock = new Object();
@GuardedBy("mServiceConnectionLock")
- private ImpressionAttestationServiceConnection mServiceConnection;
+ private ScreenshotHasherServiceConnection mServiceConnection;
private final Context mContext;
/**
- * Lock used for the cached {@link #mImpressionAlgorithms} array
+ * Lock used for the cached {@link #mHashingAlgorithms} array
*/
- private final Object mImpressionAlgorithmsLock = new Object();
+ private final Object mHashingAlgorithmsLock = new Object();
- @GuardedBy("mImpressionAlgorithmsLock")
- private String[] mImpressionAlgorithms;
+ @GuardedBy("mHashingAlgorithmsLock")
+ private String[] mHashingAlgorithms;
private final Handler mHandler;
@@ -93,24 +94,24 @@
private final RectF mTmpRectF = new RectF();
private interface Command {
- void run(IImpressionAttestationService service) throws RemoteException;
+ void run(IScreenshotHasherService service) throws RemoteException;
}
- ImpressionAttestationController(Context context) {
+ ScreenshotHashController(Context context) {
mContext = context;
mHandler = new Handler(Looper.getMainLooper());
mSalt = UUID.randomUUID().toString().getBytes();
}
- String[] getSupportedImpressionAlgorithms() {
- // We have a separate lock for the impression algorithm array since it doesn't need to make
+ String[] getSupportedHashingAlgorithms() {
+ // We have a separate lock for the hashing algorithm array since it doesn't need to make
// the request through the service connection. Instead, we have a lock to ensure we can
- // properly cache the impression algorithms array so we don't need to call into the
+ // properly cache the hashing algorithms array so we don't need to call into the
// ExtServices process for each request.
- synchronized (mImpressionAlgorithmsLock) {
+ synchronized (mHashingAlgorithmsLock) {
// Already have cached values
- if (mImpressionAlgorithms != null) {
- return mImpressionAlgorithms;
+ if (mHashingAlgorithms != null) {
+ return mHashingAlgorithms;
}
final ServiceInfo serviceInfo = getServiceInfo();
@@ -127,54 +128,55 @@
final int resourceId = serviceInfo.metaData.getInt(
SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
- mImpressionAlgorithms = res.getStringArray(resourceId);
+ mHashingAlgorithms = res.getStringArray(resourceId);
- return mImpressionAlgorithms;
+ return mHashingAlgorithms;
}
}
- boolean verifyImpressionToken(ImpressionToken impressionToken) {
+ boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
final SyncCommand syncCommand = new SyncCommand();
Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
- service.verifyImpressionToken(mSalt, impressionToken, remoteCallback);
+ service.verifyScreenshotHash(mSalt, screenshotHash, remoteCallback);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke verifyImpressionToken command");
+ Slog.e(TAG, "Failed to invoke verifyScreenshotHash command");
}
});
- return results.getBoolean(ImpressionAttestationService.EXTRA_VERIFICATION_STATUS);
+ return results.getBoolean(EXTRA_VERIFICATION_STATUS);
}
- ImpressionToken generateImpressionToken(HardwareBuffer screenshot, Rect bounds,
+ ScreenshotHash generateScreenshotHash(HardwareBuffer screenshot, Rect bounds,
String hashAlgorithm) {
final SyncCommand syncCommand = new SyncCommand();
Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
- service.generateImpressionToken(mSalt, screenshot, bounds, hashAlgorithm,
+ service.generateScreenshotHash(mSalt, screenshot, bounds, hashAlgorithm,
remoteCallback);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke generateImpressionToken command", e);
+ Slog.e(TAG, "Failed to invoke generateScreenshotHash command", e);
}
});
- return results.getParcelable(ImpressionAttestationService.EXTRA_IMPRESSION_TOKEN);
+ return results.getParcelable(EXTRA_SCREENSHOT_HASH);
}
/**
- * Calculate the bounds to take the screenshot when generating the impression token. This takes
- * into account window transform, magnification, and display bounds.
+ * Calculate the bounds to take the screenshot when generating the ScreenshotHash. This
+ * takes into account window transform, magnification, and display bounds.
*
* Call while holding {@link WindowManagerService#mGlobalLock}
*
- * @param win Window that the impression token is generated for.
+ * @param win Window that the ScreenshotHash is generated for.
* @param boundsInWindow The bounds passed in about where in the window to take the screenshot.
* @param outBounds The result of the calculated bounds
*/
- void calculateImpressionTokenBoundsLocked(WindowState win, Rect boundsInWindow,
+ void calculateScreenshotHashBoundsLocked(WindowState win, Rect boundsInWindow,
Rect outBounds) {
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsInWindow=" + boundsInWindow);
+ Slog.d(TAG,
+ "calculateScreenshotHashBoundsLocked: boundsInWindow=" + boundsInWindow);
}
outBounds.set(boundsInWindow);
@@ -192,7 +194,8 @@
outBounds.intersectUnchecked(windowBounds);
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsIntersectWindow=" + outBounds);
+ Slog.d(TAG,
+ "calculateScreenshotHashBoundsLocked: boundsIntersectWindow=" + outBounds);
}
if (outBounds.isEmpty()) {
@@ -207,7 +210,7 @@
outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right,
(int) mTmpRectF.bottom);
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsInDisplay=" + outBounds);
+ Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsInDisplay=" + outBounds);
}
// Apply the magnification spec values to the bounds since the content could be magnified
@@ -218,7 +221,8 @@
}
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsWithMagnification=" + outBounds);
+ Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsWithMagnification="
+ + outBounds);
}
if (outBounds.isEmpty()) {
@@ -230,7 +234,7 @@
final Rect displayBounds = displayContent.getBounds();
outBounds.intersectUnchecked(displayBounds);
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: finalBounds=" + outBounds);
+ Slog.d(TAG, "calculateScreenshotHashBoundsLocked: finalBounds=" + outBounds);
}
}
@@ -244,7 +248,7 @@
if (DEBUG) Slog.v(TAG, "creating connection");
// Create the connection
- mServiceConnection = new ImpressionAttestationServiceConnection();
+ mServiceConnection = new ScreenshotHasherServiceConnection();
final ComponentName component = getServiceComponentName();
if (DEBUG) Slog.v(TAG, "binding to: " + component);
@@ -275,7 +279,7 @@
return null;
}
- final Intent intent = new Intent(ImpressionAttestationService.SERVICE_INTERFACE);
+ final Intent intent = new Intent(ScreenshotHasherService.SERVICE_INTERFACE);
intent.setPackage(packageName);
final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
@@ -292,10 +296,10 @@
if (serviceInfo == null) return null;
final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- if (!Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE
+ if (!Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE
.equals(serviceInfo.permission)) {
Slog.w(TAG, name.flattenToShortString() + " requires permission "
- + Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE);
+ + Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE);
return null;
}
@@ -308,7 +312,7 @@
private Bundle mResult;
private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
- public Bundle run(BiConsumer<IImpressionAttestationService, RemoteCallback> func) {
+ public Bundle run(BiConsumer<IScreenshotHasherService, RemoteCallback> func) {
connectAndRun(service -> {
RemoteCallback callback = new RemoteCallback(result -> {
mResult = result;
@@ -327,9 +331,9 @@
}
}
- private class ImpressionAttestationServiceConnection implements ServiceConnection {
+ private class ScreenshotHasherServiceConnection implements ServiceConnection {
@GuardedBy("mServiceConnectionLock")
- private IImpressionAttestationService mRemoteService;
+ private IScreenshotHasherService mRemoteService;
@GuardedBy("mServiceConnectionLock")
private ArrayList<Command> mQueuedCommands;
@@ -338,7 +342,7 @@
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name);
synchronized (mServiceConnectionLock) {
- mRemoteService = IImpressionAttestationService.Stub.asInterface(service);
+ mRemoteService = IScreenshotHasherService.Stub.asInterface(service);
if (mQueuedCommands != null) {
final int size = mQueuedCommands.size();
if (DEBUG) Slog.d(TAG, "running " + size + " queued commands");
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b1606c5..1f8daf6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -22,7 +22,6 @@
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
-import static android.Manifest.permission.USE_BACKGROUND_BLUR;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
@@ -58,7 +57,7 @@
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.MergedConfiguration;
@@ -103,13 +102,14 @@
private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();
private final DragDropController mDragDropController;
final boolean mCanAddInternalSystemWindow;
+ private final boolean mCanStartTasksFromRecents;
+
// If non-system overlays from this process can be hidden by the user or app using
// HIDE_NON_SYSTEM_OVERLAY_WINDOWS.
final boolean mOverlaysCanBeHidden;
final boolean mCanCreateSystemApplicationOverlay;
final boolean mCanHideNonSystemOverlayWindows;
final boolean mCanAcquireSleepToken;
- final boolean mCanUseBackgroundBlur;
private AlertWindowNotification mAlertWindowNotification;
private boolean mShowingAlertWindowNotificationAllowed;
private boolean mClientDead = false;
@@ -134,12 +134,12 @@
mCanCreateSystemApplicationOverlay =
service.mContext.checkCallingOrSelfPermission(SYSTEM_APPLICATION_OVERLAY)
== PERMISSION_GRANTED;
+ mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission(
+ START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED;
mOverlaysCanBeHidden = !mCanAddInternalSystemWindow
&& !mService.mAtmInternal.isCallerRecents(mUid);
mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
== PERMISSION_GRANTED;
- mCanUseBackgroundBlur = service.mContext.checkCallingOrSelfPermission(USE_BACKGROUND_BLUR)
- == PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
mDragDropController = mService.mDragDropController;
StringBuilder sb = new StringBuilder();
@@ -192,32 +192,30 @@
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
+ int viewVisibility, int displayId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
- UserHandle.getUserId(mUid), requestedVisibility, outFrame, outInputChannel,
- outInsetsState, outActiveControls);
+ UserHandle.getUserId(mUid), requestedVisibility, outInputChannel, outInsetsState,
+ outActiveControls);
}
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
- Rect outFrame, InputChannel outInputChannel, InsetsState outInsetsState,
+ InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
- requestedVisibility, outFrame, outInputChannel, outInsetsState,
- outActiveControls);
+ requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
}
@Override
public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, InsetsState outInsetsState) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
- UserHandle.getUserId(mUid), mDummyRequestedVisibility,
- new Rect() /* outFrame */, null /* outInputChannel */, outInsetsState,
- mDummyControls);
+ UserHandle.getUserId(mUid), mDummyRequestedVisibility, null /* outInputChannel */,
+ outInsetsState, mDummyControls);
}
@Override
@@ -374,8 +372,9 @@
} else if (hasShortcut) {
// Restrict who can start a shortcut drag since it will start the shortcut as the
// target shortcut package
- mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
- "performDrag");
+ if (!mCanStartTasksFromRecents) {
+ throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
+ }
for (int i = 0; i < data.getItemCount(); i++) {
final ClipData.Item item = data.getItemAt(i);
final Intent intent = item.getIntent();
@@ -403,8 +402,9 @@
}
} else if (hasTask) {
// TODO(b/169894807): Consider opening this up for tasks from the same app as the caller
- mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
- "performDrag");
+ if (!mCanStartTasksFromRecents) {
+ throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
+ }
for (int i = 0; i < data.getItemCount(); i++) {
final ClipData.Item item = data.getItemAt(i);
final Intent intent = item.getIntent();
@@ -860,11 +860,11 @@
}
@Override
- public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+ public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
String hashAlgorithm) {
final long origId = Binder.clearCallingIdentity();
try {
- return mService.generateImpressionToken(this, window, boundsInWindow, hashAlgorithm);
+ return mService.generateScreenshotHash(this, window, boundsInWindow, hashAlgorithm);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6a3110f..57ba915 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -77,6 +77,7 @@
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -144,6 +145,7 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -2090,7 +2092,9 @@
td.setEnsureNavigationBarContrastWhenTransparent(
atd.getEnsureNavigationBarContrastWhenTransparent());
}
-
+ if (td.getBackgroundColorFloating() == 0) {
+ td.setBackgroundColorFloating(atd.getBackgroundColorFloating());
+ }
}
// End search once we get to root.
@@ -2878,6 +2882,16 @@
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
outBounds.setEmpty();
computeLetterboxBounds(outBounds, newParentConfig);
+ // Since the task is letterboxed due to mismatched orientation against its parent,
+ // sandbox max bounds to the app bounds.
+ if (!outBounds.isEmpty()) {
+ if (DEBUG_CONFIGURATION) {
+ ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched "
+ + "orientation with parent, to %s vs DisplayArea %s", outBounds,
+ getDisplayArea() != null ? getDisplayArea().getBounds() : "null");
+ }
+ getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds);
+ }
}
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
@@ -3082,6 +3096,20 @@
return parentTask == null ? this : parentTask.getRootTask();
}
+ /** @return the first organized task. */
+ @Nullable
+ Task getOrganizedTask() {
+ if (isOrganized()) {
+ return this;
+ }
+ final WindowContainer parent = getParent();
+ if (parent == null) {
+ return null;
+ }
+ final Task parentTask = parent.asTask();
+ return parentTask == null ? null : parentTask.getOrganizedTask();
+ }
+
// TODO(task-merge): Figure out what's the right thing to do for places that used it.
boolean isRootTask() {
return getRootTask() == this;
@@ -4172,6 +4200,14 @@
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
+ info.topActivityToken = mReuseActivitiesReport.top != null
+ ? mReuseActivitiesReport.top.appToken
+ : null;
+ // Whether the direct top activity is in size compat mode on foreground.
+ info.topActivityInSizeCompat = mReuseActivitiesReport.top != null
+ && mReuseActivitiesReport.top.getOrganizedTask() == this
+ && mReuseActivitiesReport.top.inSizeCompatMode()
+ && mReuseActivitiesReport.top.isState(RESUMED);
info.launchCookies.clear();
info.addLaunchCookie(mLaunchCookie);
forAllActivities(r -> {
@@ -5221,6 +5257,12 @@
}
}
+ /** Called when the top activity in the Root Task enters or exits size compat mode. */
+ void onSizeCompatActivityChanged() {
+ // Trigger TaskInfoChanged to update the size compat restart button.
+ dispatchTaskInfoChangedIfNeeded(true /* force */);
+ }
+
/**
* See {@link WindowContainerTransaction#setBoundsChangeTransaction}. In short this
* transaction will be consumed by the next BASE_APPLICATION window within our hierarchy
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index b3e0108..9fac3f0 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
@@ -33,6 +34,7 @@
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.RemoteException;
import android.util.Slog;
import android.view.SurfaceControl;
@@ -357,8 +359,14 @@
mGlobalLock = atm.mGlobalLock;
}
- private void enforceTaskPermission(String func) {
- mService.enforceTaskPermission(func);
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 09df71c..07610ab 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -226,9 +226,8 @@
}
int displayId = activity.getDisplayContent().getDisplayId();
try {
- final int res = session.addToDisplay(window, layoutParams,
- View.GONE, displayId, mTmpInsetsState, tmpFrames.frame,
- null /* outInputChannel */, mTmpInsetsState, mTempControls);
+ final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
+ mTmpInsetsState, null /* outInputChannel */, mTmpInsetsState, mTempControls);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return null;
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index a5ebf9a..015a0fb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -49,6 +49,10 @@
static final String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS =
"system_gesture_exclusion_log_debounce_millis";
+ // Enable logging from the sensor which publishes accel and gyro data generating a rotation
+ // event
+ private static final String KEY_RAW_SENSOR_LOGGING_ENABLED = "raw_sensor_logging_enabled";
+
private static final int MIN_GESTURE_EXCLUSION_LIMIT_DP = 200;
/** @see #KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS */
@@ -58,6 +62,8 @@
/** @see AndroidDeviceConfig#KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE */
boolean mSystemGestureExcludedByPreQStickyImmersive;
+ boolean mRawSensorLoggingEnabled;
+
private final WindowManagerGlobalLock mGlobalLock;
private final Runnable mUpdateSystemGestureExclusionCallback;
private final DeviceConfigInterface mDeviceConfig;
@@ -133,6 +139,9 @@
case KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS:
updateSystemGestureExclusionLogDebounceMillis();
break;
+ case KEY_RAW_SENSOR_LOGGING_ENABLED:
+ updateRawSensorDataLoggingEnabled();
+ break;
default:
break;
}
@@ -158,6 +167,12 @@
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
}
+ private void updateRawSensorDataLoggingEnabled() {
+ mRawSensorLoggingEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_RAW_SENSOR_LOGGING_ENABLED, false);
+ }
+
void dump(PrintWriter pw) {
pw.println("WINDOW MANAGER CONSTANTS (dumpsys window constants):");
@@ -167,6 +182,8 @@
pw.print("="); pw.println(mSystemGestureExclusionLimitDp);
pw.print(" "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE);
pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
+ pw.print(" "); pw.print(KEY_RAW_SENSOR_LOGGING_ENABLED);
+ pw.print("="); pw.println(mRawSensorLoggingEnabled);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8e6a778..673c6a5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -160,6 +160,7 @@
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -201,7 +202,7 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.sysprop.SurfaceFlingerProperties;
@@ -457,7 +458,8 @@
*/
static final float MIN_TASK_LETTERBOX_ASPECT_RATIO = 1.0f;
- final WindowManagerConstants mConstants;
+ @VisibleForTesting
+ WindowManagerConstants mConstants;
final WindowTracing mWindowTracing;
@@ -767,7 +769,7 @@
final EmbeddedWindowController mEmbeddedWindowController;
final AnrController mAnrController;
- private final ImpressionAttestationController mImpressionAttestationController;
+ private final ScreenshotHashController mScreenshotHashController;
private final WindowContextListenerController mWindowContextListenerController =
new WindowContextListenerController();
@@ -1010,10 +1012,30 @@
// Aspect ratio of task level letterboxing, values <= MIN_TASK_LETTERBOX_ASPECT_RATIO will be
// ignored.
- private float mTaskLetterboxAspectRatio;
+ private volatile float mTaskLetterboxAspectRatio;
+
+ /** Enum for Letterbox background type. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND,
+ LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING})
+ @interface LetterboxBackgroundType {};
+ /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */
+ static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0;
+
+ /** Color specified in R.attr.colorBackground for the letterboxed application. */
+ static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND = 1;
+
+ /** Color specified in R.attr.colorBackgroundFloating for the letterboxed application. */
+ static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING = 2;
// Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
- private int mLetterboxActivityCornersRadius;
+ private volatile int mLetterboxActivityCornersRadius;
+
+ // Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
+ private volatile Color mLetterboxBackgroundColor;
+
+ @LetterboxBackgroundType
+ private volatile int mLetterboxBackgroundType;
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
@@ -1240,10 +1262,15 @@
com.android.internal.R.bool.config_perDisplayFocusEnabled);
mAssistantOnTopOfDream = context.getResources().getBoolean(
com.android.internal.R.bool.config_assistantOnTopOfDream);
+
mTaskLetterboxAspectRatio = context.getResources().getFloat(
com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
mLetterboxActivityCornersRadius = context.getResources().getInteger(
com.android.internal.R.integer.config_letterboxActivityCornersRadius);
+ mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
+ com.android.internal.R.color.config_letterboxBackgroundColor));
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(context);
+
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -1392,7 +1419,7 @@
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
- mImpressionAttestationController = new ImpressionAttestationController(mContext);
+ mScreenshotHashController = new ScreenshotHashController(mContext);
setGlobalShadowSettings();
mAnrController = new AnrController(this);
mStartingSurfaceController = new StartingSurfaceController(this);
@@ -1461,7 +1488,7 @@
}
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
- int displayId, int requestUserId, InsetsState requestedVisibility, Rect outFrame,
+ int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
Arrays.fill(outActiveControls, null);
@@ -1652,7 +1679,7 @@
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
- session.mCanAddInternalSystemWindow, session.mCanUseBackgroundBlur);
+ session.mCanAddInternalSystemWindow);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
@@ -1803,7 +1830,7 @@
prepareNoneTransitionForRelaunching(activity);
}
- if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outInsetsState,
+ if (displayPolicy.getLayoutHint(win.mAttrs, token, outInsetsState,
win.isClientLocal())) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
@@ -2568,11 +2595,12 @@
} else if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = true;
win.mAnimatingExit = true;
- } else if (win.isAnimating(TRANSITION | PARENTS)) {
+ } else if (win.mDisplayContent.okToAnimate() && win.isAnimating(TRANSITION | PARENTS)) {
// Currently in a hide animation... turn this into
// an exit.
win.mAnimatingExit = true;
- } else if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
+ } else if (win.mDisplayContent.okToAnimate()
+ && win.mDisplayContent.mWallpaperController.isWallpaperTarget(win)) {
// If the wallpaper is currently behind this
// window, we need to change both of them inside
// of a transaction to avoid artifacts.
@@ -3901,14 +3929,7 @@
* the framework implementation will be used to determine the aspect ratio.
*/
void setTaskLetterboxAspectRatio(float aspectRatio) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mTaskLetterboxAspectRatio = aspectRatio;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mTaskLetterboxAspectRatio = aspectRatio;
}
/**
@@ -3916,29 +3937,15 @@
* com.android.internal.R.dimen.config_taskLetterboxAspectRatio}.
*/
void resetTaskLetterboxAspectRatio() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mTaskLetterboxAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mTaskLetterboxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
}
/**
* Gets the aspect ratio of task level letterboxing.
*/
float getTaskLetterboxAspectRatio() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- return mTaskLetterboxAspectRatio;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ return mTaskLetterboxAspectRatio;
}
/**
@@ -3948,14 +3955,7 @@
* and corners of the activity won't be rounded.
*/
void setLetterboxActivityCornersRadius(int cornersRadius) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mLetterboxActivityCornersRadius = cornersRadius;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mLetterboxActivityCornersRadius = cornersRadius;
}
/**
@@ -3963,15 +3963,8 @@
* com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
*/
void resetLetterboxActivityCornersRadius() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_letterboxActivityCornersRadius);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxActivityCornersRadius);
}
/**
@@ -3985,20 +3978,75 @@
* Gets corners raidus for activities presented in the letterbox mode.
*/
int getLetterboxActivityCornersRadius() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- return mLetterboxActivityCornersRadius;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ return mLetterboxActivityCornersRadius;
+ }
+
+ /**
+ * Gets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ Color getLetterboxBackgroundColor() {
+ return mLetterboxBackgroundColor;
+ }
+
+
+ /**
+ * Sets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ void setLetterboxBackgroundColor(Color color) {
+ mLetterboxBackgroundColor = color;
+ }
+
+ /**
+ * Resets color of letterbox background to {@link
+ * com.android.internal.R.color.config_letterboxBackgroundColor}.
+ */
+ void resetLetterboxBackgroundColor() {
+ mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
+ com.android.internal.R.color.config_letterboxBackgroundColor));
+ }
+
+ /**
+ * Gets {@link LetterboxBackgroundType} specified in {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
+ */
+ @LetterboxBackgroundType
+ int getLetterboxBackgroundType() {
+ return mLetterboxBackgroundType;
+ }
+
+ /** Sets letterbox background type. */
+ void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+ mLetterboxBackgroundType = backgroundType;
+ }
+
+ /**
+ * Resets cletterbox background type to {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType}.
+ */
+ void resetLetterboxBackgroundType() {
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+ }
+
+ @LetterboxBackgroundType
+ private static int readLetterboxBackgroundTypeFromConfig(Context context) {
+ int backgroundType = context.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxBackgroundType);
+ return backgroundType == LETTERBOX_BACKGROUND_SOLID_COLOR
+ || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND
+ || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING
+ ? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
}
@Override
public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(
- android.Manifest.permission.SET_ORIENTATION, "setIgnoreOrientationRequest()");
+ if (!checkCallingPermission(
+ android.Manifest.permission.SET_ORIENTATION, "setIgnoreOrientationRequest()")) {
+ throw new SecurityException("Requires SET_ORIENTATION permission");
+ }
final long origId = Binder.clearCallingIdentity();
try {
@@ -6061,8 +6109,10 @@
@Override
public void setRecentsVisibility(boolean visible) {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
- "setRecentsVisibility()");
+ if (!checkCallingPermission(
+ android.Manifest.permission.STATUS_BAR, "setRecentsVisibility()")) {
+ throw new SecurityException("Requires STATUS_BAR permission");
+ }
synchronized (mGlobalLock) {
mPolicy.setRecentsVisibilityLw(visible);
}
@@ -6070,8 +6120,11 @@
@Override
public void hideTransientBars(int displayId) {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
- "hideTransientBars()");
+ if (!checkCallingPermission(
+ android.Manifest.permission.STATUS_BAR, "hideTransientBars()")) {
+ throw new SecurityException("Requires STATUS_BAR permission");
+ }
+
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
@@ -8373,8 +8426,11 @@
/** Return whether layer tracing is enabled */
public boolean isLayerTracing() {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
- "isLayerTracing");
+ if (!checkCallingPermission(
+ android.Manifest.permission.DUMP, "isLayerTracing()")) {
+ throw new SecurityException("Requires DUMP permission");
+ }
+
final long token = Binder.clearCallingIdentity();
try {
Parcel data = null;
@@ -8406,8 +8462,11 @@
/** Enable or disable layer tracing */
public void setLayerTracing(boolean enabled) {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
- "setLayerTracing");
+ if (!checkCallingPermission(
+ android.Manifest.permission.DUMP, "setLayerTracing()")) {
+ throw new SecurityException("Requires DUMP permission");
+ }
+
final long token = Binder.clearCallingIdentity();
try {
Parcel data = null;
@@ -8433,8 +8492,11 @@
/** Set layer tracing flags. */
public void setLayerTracingFlags(int flags) {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
- "setLayerTracingFlags");
+ if (!checkCallingPermission(
+ android.Manifest.permission.DUMP, "setLayerTracingFlags")) {
+ throw new SecurityException("Requires DUMP permission");
+ }
+
final long token = Binder.clearCallingIdentity();
try {
Parcel data = null;
@@ -8494,8 +8556,8 @@
+ "could not be found!");
}
final WindowToken windowToken = dc.getWindowToken(attrs.token);
- return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken,
- mTmpRect /* outFrame */, outInsetsState, fromLocal);
+ return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken, outInsetsState,
+ fromLocal);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -8600,38 +8662,38 @@
}
@Override
- public String[] getSupportedImpressionAlgorithms() {
- return mImpressionAttestationController.getSupportedImpressionAlgorithms();
+ public String[] getSupportedScreenshotHashingAlgorithms() {
+ return mScreenshotHashController.getSupportedHashingAlgorithms();
}
@Override
- public boolean verifyImpressionToken(ImpressionToken impressionToken) {
- return mImpressionAttestationController.verifyImpressionToken(impressionToken);
+ public boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
+ return mScreenshotHashController.verifyScreenshotHash(screenshotHash);
}
- ImpressionToken generateImpressionToken(Session session, IWindow window,
+ ScreenshotHash generateScreenshotHash(Session session, IWindow window,
Rect boundsInWindow, String hashAlgorithm) {
final SurfaceControl displaySurfaceControl;
final Rect boundsInDisplay = new Rect(boundsInWindow);
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, window, false);
if (win == null) {
- Slog.w(TAG, "Failed to generate impression token. Invalid window");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Invalid window");
return null;
}
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
- Slog.w(TAG, "Failed to generate impression token. Window is not on a display");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Window is not on a display");
return null;
}
displaySurfaceControl = displayContent.getSurfaceControl();
- mImpressionAttestationController.calculateImpressionTokenBoundsLocked(win,
+ mScreenshotHashController.calculateScreenshotHashBoundsLocked(win,
boundsInWindow, boundsInDisplay);
if (boundsInDisplay.isEmpty()) {
- Slog.w(TAG, "Failed to generate impression token. Bounds are not on screen");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Bounds are not on screen");
return null;
}
}
@@ -8651,11 +8713,11 @@
SurfaceControl.captureLayers(args);
if (screenshotHardwareBuffer == null
|| screenshotHardwareBuffer.getHardwareBuffer() == null) {
- Slog.w(TAG, "Failed to generate impression token. Failed to take screenshot");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Failed to take screenshot");
return null;
}
- return mImpressionAttestationController.generateImpressionToken(
+ return mScreenshotHashController.generateScreenshotHash(
screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index badd29a..645786c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -18,6 +18,11 @@
import static android.os.Build.IS_USER;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
+
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
@@ -34,6 +39,7 @@
import com.android.internal.protolog.ProtoLogImpl;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
import java.io.IOException;
import java.io.PrintWriter;
@@ -119,6 +125,14 @@
return runSetLetterboxActivityCornersRadius(pw);
case "get-letterbox-activity-corners-radius":
return runGetLetterboxActivityCornersRadius(pw);
+ case "set-letterbox-background-type":
+ return runSetLetterboxBackgroundType(pw);
+ case "get-letterbox-background-type":
+ return runGetLetterboxBackgroundType(pw);
+ case "set-letterbox-background-color":
+ return runSetLetterboxBackgroundColor(pw);
+ case "get-letterbox-background-color":
+ return runGetLetterboxBackgroundColor(pw);
case "reset":
return runReset(pw);
default:
@@ -581,6 +595,79 @@
return 0;
}
+ private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+ @LetterboxBackgroundType final int backgroundType;
+
+ String arg = getNextArgRequired();
+ if ("reset".equals(arg)) {
+ mInternal.resetLetterboxBackgroundType();
+ return 0;
+ }
+ switch (arg) {
+ case "solid_color":
+ backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
+ break;
+ case "app_color_background":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+ break;
+ case "app_color_background_floating":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'reset', 'solid_color' or 'app_color_background' should "
+ + "be provided as an argument");
+ return -1;
+ }
+
+ mInternal.setLetterboxBackgroundType(backgroundType);
+ return 0;
+ }
+
+ private int runGetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+ @LetterboxBackgroundType final int backgroundType = mInternal.getLetterboxBackgroundType();
+ switch (backgroundType) {
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ pw.println("Letterbox background type is 'solid_color'");
+ break;
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ pw.println("Letterbox background type is 'app_color_background'");
+ break;
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ pw.println("Letterbox background type is 'app_color_background_floating'");
+ break;
+ default:
+ throw new AssertionError("Unexpected letterbox background type: " + backgroundType);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+ final Color color;
+ String arg = getNextArgRequired();
+ try {
+ if ("reset".equals(arg)) {
+ mInternal.resetLetterboxBackgroundColor();
+ return 0;
+ }
+ color = Color.valueOf(Color.parseColor(arg));
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or color in #RRGGBB format should be provided as "
+ + "an argument " + e + " but got " + arg);
+ return -1;
+ }
+
+ mInternal.setLetterboxBackgroundColor(color);
+ return 0;
+ }
+
+ private int runGetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+ final Color color = mInternal.getLetterboxBackgroundColor();
+ pw.println("Letterbox background color is " + Integer.toHexString(color.toArgb()));
+ return 0;
+ }
+
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -611,6 +698,12 @@
// set-letterbox-activity-corners-radius
mInternal.resetLetterboxActivityCornersRadius();
+ // set-letterbox-background-type
+ mInternal.resetLetterboxBackgroundType();
+
+ // set-letterbox-background-color
+ mInternal.resetLetterboxBackgroundColor();
+
pw.println("Reset all settings for displayId=" + displayId);
return 0;
}
@@ -652,6 +745,16 @@
pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
pw.println(" ignored and corners of the activity won't be rounded.");
+ pw.println(" set-letterbox-background-color [reset|colorName|'\\#RRGGBB']");
+ pw.println(" get-letterbox-background-color");
+ pw.println(" Color of letterbox background which is be used when letterbox background");
+ pw.println(" type is 'solid-color'. Use get(set)-letterbox-background-type to check");
+ pw.println(" and control letterbox background type. See Color#parseColor for allowed");
+ pw.println(" color formats (#RRGGBB and some colors by name, e.g. magenta or olive). ");
+ pw.println(" set-letterbox-background-type [reset|solid_color|app_color_background");
+ pw.println(" |app_color_background_floating]");
+ pw.println(" get-letterbox-background-type");
+ pw.println(" Type of background used in the letterbox mode.");
pw.println(" reset [-d DISPLAY_ID]");
pw.println(" Reset all override settings.");
if (!IS_USER) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 1b81914..bd7116a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -40,6 +40,7 @@
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
@@ -112,6 +113,16 @@
}
@Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
+ }
+ }
+
+ @Override
public void applyTransaction(WindowContainerTransaction t) {
enforceTaskPermission("applyTransaction()");
if (t == null) {
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
similarity index 96%
rename from services/core/java/com/android/server/policy/WindowOrientationListener.java
rename to services/core/java/com/android/server/wm/WindowOrientationListener.java
index 17e81da..da31bb2 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.wm;
import static com.android.server.wm.WindowOrientationListenerProto.ENABLED;
import static com.android.server.wm.WindowOrientationListenerProto.ROTATION;
@@ -31,6 +31,8 @@
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.internal.util.FrameworkStatsLog;
+
import java.io.PrintWriter;
import java.util.List;
@@ -63,6 +65,7 @@
private OrientationJudge mOrientationJudge;
private int mCurrentRotation = -1;
private final Context mContext;
+ private final WindowManagerConstants mConstants;
private final Object mLock = new Object();
@@ -71,9 +74,11 @@
*
* @param context for the WindowOrientationListener.
* @param handler Provides the Looper for receiving sensor updates.
+ * @param wmService WindowManagerService to read the device config from.
*/
- public WindowOrientationListener(Context context, Handler handler) {
- this(context, handler, SensorManager.SENSOR_DELAY_UI);
+ public WindowOrientationListener(
+ Context context, Handler handler, WindowManagerService wmService) {
+ this(context, handler, wmService, SensorManager.SENSOR_DELAY_UI);
}
/**
@@ -81,6 +86,7 @@
*
* @param context for the WindowOrientationListener.
* @param handler Provides the Looper for receiving sensor updates.
+ * @param wmService WindowManagerService to read the device config from.
* @param rate at which sensor events are processed (see also
* {@link android.hardware.SensorManager SensorManager}). Use the default
* value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
@@ -88,10 +94,12 @@
*
* This constructor is private since no one uses it.
*/
- private WindowOrientationListener(Context context, Handler handler, int rate) {
+ private WindowOrientationListener(
+ Context context, Handler handler, WindowManagerService wmService, int rate) {
mContext = context;
mHandler = handler;
- mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+ mConstants = wmService.mConstants;
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
mRate = rate;
List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION);
Sensor wakeUpDeviceOrientationSensor = null;
@@ -497,7 +505,7 @@
private static final float MIN_ACCELERATION_MAGNITUDE =
SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE;
private static final float MAX_ACCELERATION_MAGNITUDE =
- SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
+ SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
// Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
// when screen is facing the sky or ground), we completely ignore orientation data
@@ -1072,8 +1080,14 @@
mDesiredRotation = reportedRotation;
newRotation = evaluateRotationChangeLocked();
}
- if (newRotation >=0) {
+ if (newRotation >= 0) {
onProposedRotationChanged(newRotation);
+ if (mConstants.mRawSensorLoggingEnabled) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DEVICE_ROTATED,
+ event.timestamp,
+ rotationToLogEnum(reportedRotation));
+ }
}
}
@@ -1180,5 +1194,20 @@
}
}
};
+
+ private int rotationToLogEnum(int rotation) {
+ switch (rotation) {
+ case 0:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_0;
+ case 1:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_90;
+ case 2:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_180;
+ case 3:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_270;
+ default:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__UNKNOWN;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 5676909..8b4d415 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -24,13 +24,11 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ActivityManagerService.MY_PID;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.Task.ActivityState.DESTROYED;
@@ -61,8 +59,6 @@
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -103,10 +99,6 @@
final String mName;
final int mUid;
- // A set of tokens that currently contribute to this process being temporarily allowed
- // to start activities even if it's not in the foreground. The values of this map are optional
- // (can be null) and are used to trace back the grant to the notification token mechanism.
- private final ArrayMap<Binder, IBinder> mBackgroundActivityStartTokens = new ArrayMap<>();
// The process of this application; 0 if none
private volatile int mPid;
// user of process.
@@ -118,6 +110,7 @@
final ArraySet<String> mPkgList = new ArraySet<>();
private final WindowProcessListener mListener;
private final ActivityTaskManagerService mAtm;
+ private final BackgroundLaunchProcessController mBgLaunchController;
// The actual proc... may be null only if 'persistent' is true (in which case we are in the
// process of launching the app)
private IApplicationThread mThread;
@@ -169,8 +162,6 @@
private volatile boolean mPerceptible;
// Set to true when process was launched with a wrapper attached
private volatile boolean mUsingWrapper;
- // Set of UIDs of clients currently bound to this process
- private volatile ArraySet<Integer> mBoundClientUids = new ArraySet<Integer>();
// Thread currently set for VR scheduling
int mVrThreadTid;
@@ -191,10 +182,10 @@
// The most recent top-most activity that was resumed in the process for pre-Q app.
private ActivityRecord mPreQTopResumedActivity = null;
// The last time an activity was launched in the process
- private long mLastActivityLaunchTime;
+ private volatile long mLastActivityLaunchTime;
// The last time an activity was finished in the process while the process participated
// in a visible task
- private long mLastActivityFinishTime;
+ private volatile long mLastActivityFinishTime;
// Last configuration that was reported to the process.
private final Configuration mLastReportedConfiguration = new Configuration();
@@ -227,9 +218,6 @@
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
private boolean mRunningRemoteAnimation;
- @Nullable
- private final BackgroundActivityStartCallback mBackgroundActivityStartCallback;
-
// The bits used for mActivityStateFlags.
private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
@@ -246,8 +234,8 @@
*/
private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
- public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
- String name, int uid, int userId, Object owner,
+ public WindowProcessController(@NonNull ActivityTaskManagerService atm,
+ @NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
@NonNull WindowProcessListener listener) {
mInfo = info;
mName = name;
@@ -256,7 +244,8 @@
mOwner = owner;
mListener = listener;
mAtm = atm;
- mBackgroundActivityStartCallback = mAtm.getBackgroundActivityStartCallback();
+ mBgLaunchController = new BackgroundLaunchProcessController(
+ atm::hasActiveVisibleWindow, atm.getBackgroundActivityStartCallback());
boolean isSysUiPackage = info.packageName.equals(
mAtm.getSysUiServiceComponentLocked().getPackageName());
@@ -489,162 +478,59 @@
}
void setLastActivityFinishTimeIfNeeded(long finishTime) {
- if (finishTime <= mLastActivityFinishTime || !hasActivityInVisibleTask()) {
+ if (finishTime <= mLastActivityFinishTime || !hasVisibleActivities()) {
return;
}
mLastActivityFinishTime = finishTime;
}
/**
- * Allows background activity starts using token {@code entity}. Optionally, you can provide
- * {@code originatingToken} if you have one such originating token, this is useful for tracing
- * back the grant in the case of the notification token.
- *
- * If {@code entity} is already added, this method will update its {@code originatingToken}.
+ * @see BackgroundLaunchProcessController#addOrUpdateAllowBackgroundActivityStartsToken(Binder,
+ * IBinder)
*/
public void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
@Nullable IBinder originatingToken) {
- synchronized (mAtm.mGlobalLock) {
- mBackgroundActivityStartTokens.put(entity, originatingToken);
- }
+ mBgLaunchController.addOrUpdateAllowBackgroundActivityStartsToken(entity, originatingToken);
}
- /**
- * Removes token {@code entity} that allowed background activity starts added via {@link
- * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}.
- */
+ /** @see BackgroundLaunchProcessController#removeAllowBackgroundActivityStartsToken(Binder) */
public void removeAllowBackgroundActivityStartsToken(Binder entity) {
- synchronized (mAtm.mGlobalLock) {
- mBackgroundActivityStartTokens.remove(entity);
- }
+ mBgLaunchController.removeAllowBackgroundActivityStartsToken(entity);
}
/**
* Is this WindowProcessController in the state of allowing background FGS start?
*/
+ @HotPath(caller = HotPath.START_SERVICE)
public boolean areBackgroundFgsStartsAllowed() {
- synchronized (mAtm.mGlobalLock) {
- return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed(), true);
- }
+ return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed(),
+ true /* isCheckingForFgsStart */);
}
boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed) {
- return areBackgroundActivityStartsAllowed(appSwitchAllowed, false);
+ return areBackgroundActivityStartsAllowed(appSwitchAllowed,
+ false /* isCheckingForFgsStart */);
}
- boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed,
+ private boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed,
boolean isCheckingForFgsStart) {
- // If app switching is not allowed, we ignore all the start activity grace period
- // exception so apps cannot start itself in onPause() after pressing home button.
- if (appSwitchAllowed) {
- // allow if any activity in the caller has either started or finished very recently, and
- // it must be started or finished after last stop app switches time.
- final long now = SystemClock.uptimeMillis();
- if (now - mLastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
- || now - mLastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
- // if activity is started and finished before stop app switch time, we should not
- // let app to be able to start background activity even it's in grace period.
- if (mLastActivityLaunchTime > mAtm.getLastStopAppSwitchesTime()
- || mLastActivityFinishTime > mAtm.getLastStopAppSwitchesTime()) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[WindowProcessController(" + mPid
- + ")] Activity start allowed: within "
- + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
- }
- return true;
- }
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[WindowProcessController(" + mPid + ")] Activity start within "
- + ACTIVITY_BG_START_GRACE_PERIOD_MS
- + "ms grace period but also within stop app switch window");
- }
-
- }
- }
- // allow if the proc is instrumenting with background activity starts privs
- if (mInstrumentingWithBackgroundActivityStartPrivileges) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[WindowProcessController(" + mPid
- + ")] Activity start allowed: process instrumenting with background "
- + "activity starts privileges");
- }
- return true;
- }
- // allow if the caller has an activity in any foreground task
- if (appSwitchAllowed && hasActivityInVisibleTask()) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[WindowProcessController(" + mPid
- + ")] Activity start allowed: process has activity in foreground task");
- }
- return true;
- }
- // allow if the caller is bound by a UID that's currently foreground
- if (isBoundByForegroundUid()) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[WindowProcessController(" + mPid
- + ")] Activity start allowed: process bound by foreground uid");
- }
- return true;
- }
- // allow if the flag was explicitly set
- if (isBackgroundStartAllowedByToken(isCheckingForFgsStart)) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[WindowProcessController(" + mPid
- + ")] Activity start allowed: process allowed by token");
- }
- return true;
- }
- return false;
- }
-
- /**
- * If there are no tokens, we don't allow *by token*. If there are tokens and
- * isCheckingForFgsStart is false, we ask the callback if the start is allowed for these tokens,
- * otherwise if there is no callback we allow.
- */
- private boolean isBackgroundStartAllowedByToken(boolean isCheckingForFgsStart) {
- if (mBackgroundActivityStartTokens.isEmpty()) {
- return false;
- }
-
- if (isCheckingForFgsStart) {
- /// The checking is for BG-FGS-start.
- return true;
- }
-
- if (mBackgroundActivityStartCallback == null) {
- // We have tokens but no callback to decide => allow
- return true;
- }
- // The callback will decide
- return mBackgroundActivityStartCallback.isActivityStartAllowed(
- mBackgroundActivityStartTokens.values(), mInfo.uid, mInfo.packageName);
+ return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName,
+ appSwitchAllowed, isCheckingForFgsStart, hasVisibleActivities(),
+ mInstrumentingWithBackgroundActivityStartPrivileges,
+ mAtm.getLastStopAppSwitchesTime(),
+ mLastActivityLaunchTime, mLastActivityFinishTime);
}
/**
* Returns whether this process is allowed to close system dialogs via a background activity
* start token that allows the close system dialogs operation (eg. notification).
*/
- public boolean canCloseSystemDialogsByToken() {
- synchronized (mAtm.mGlobalLock) {
- return !mBackgroundActivityStartTokens.isEmpty()
- && mBackgroundActivityStartCallback != null
- && mBackgroundActivityStartCallback.canCloseSystemDialogs(
- mBackgroundActivityStartTokens.values(), mInfo.uid);
- }
- }
-
- private boolean isBoundByForegroundUid() {
- for (int i = mBoundClientUids.size() - 1; i >= 0; --i) {
- if (mAtm.hasActiveVisibleWindow(mBoundClientUids.valueAt(i))) {
- return true;
- }
- }
- return false;
+ boolean canCloseSystemDialogsByToken() {
+ return mBgLaunchController.canCloseSystemDialogsByToken(mUid);
}
public void setBoundClientUids(ArraySet<Integer> boundClientUids) {
- mBoundClientUids = boundClientUids;
+ mBgLaunchController.setBoundClientUids(boundClientUids);
}
/**
@@ -793,20 +679,6 @@
return displayArea;
}
- private boolean hasActivityInVisibleTask() {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- Task task = mActivities.get(i).getTask();
- if (task == null) {
- continue;
- }
- ActivityRecord topActivity = task.getTopNonFinishingActivity();
- if (topActivity != null && topActivity.mVisibleRequested) {
- return true;
- }
- }
- return false;
- }
-
/**
* Update the top resuming activity in process for pre-Q apps, only the top-most visible
* activities are allowed to be resumed per process.
@@ -1701,17 +1573,8 @@
if (mVrThreadTid != 0) {
pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
}
- if (mBackgroundActivityStartTokens.size() > 0) {
- pw.print(prefix);
- pw.println("Background activity start tokens (token: originating token):");
- for (int i = 0; i < mBackgroundActivityStartTokens.size(); i++) {
- pw.print(prefix); pw.print(" - ");
- pw.print(mBackgroundActivityStartTokens.keyAt(i));
- pw.print(": ");
- pw.println(mBackgroundActivityStartTokens.valueAt(i));
- }
- }
+ mBgLaunchController.dump(pw, prefix);
}
pw.println(prefix + " Configuration=" + getConfiguration());
pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration());
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9a7823e..661118f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -212,6 +212,7 @@
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;
@@ -296,8 +297,6 @@
final int mShowUserId;
/** The owner has {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} */
final boolean mOwnerCanAddInternalSystemWindow;
- /** The owner has {@link android.Manifest.permission#USE_BACKGROUND_BLUR} */
- final boolean mOwnerCanUseBackgroundBlur;
final WindowId mWindowId;
WindowToken mToken;
// The same object as mToken if this is an app window and null for non-app windows.
@@ -440,6 +439,7 @@
float mGlobalScale=1;
float mLastGlobalScale=1;
float mInvGlobalScale=1;
+ float mOverrideScale = 1;
float mHScale=1, mVScale=1;
float mLastHScale=1, mLastVScale=1;
final Matrix mTmpMatrix = new Matrix();
@@ -647,9 +647,14 @@
boolean mSeamlesslyRotated = false;
/**
- * Indicates if this window is behind IME. Only windows behind IME can get insets from IME.
+ * The insets state of sources provided by windows above the current window.
*/
- boolean mBehindIme = false;
+ InsetsState mAboveInsetsState = new InsetsState();
+
+ /**
+ * The insets sources provided by this window.
+ */
+ ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>();
/**
* Surface insets from the previous call to relayout(), used to track
@@ -890,11 +895,9 @@
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
- int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
- boolean ownerCanUseBackgroundBlur) {
+ int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) {
this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId,
- ownerCanAddInternalSystemWindow, ownerCanUseBackgroundBlur,
- new PowerManagerWrapper() {
+ ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
@Override
public void wakeUp(long time, @WakeReason int reason, String details) {
service.mPowerManager.wakeUp(time, reason, details);
@@ -910,7 +913,7 @@
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
- boolean ownerCanUseBackgroundBlur, PowerManagerWrapper powerManagerWrapper) {
+ PowerManagerWrapper powerManagerWrapper) {
super(service);
mTmpTransaction = service.mTransactionFactory.get();
mSession = s;
@@ -921,7 +924,6 @@
mOwnerUid = ownerId;
mShowUserId = showUserId;
mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
- mOwnerCanUseBackgroundBlur = ownerCanUseBackgroundBlur;
mWindowId = new WindowId(this);
mAttrs.copyFrom(a);
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
@@ -1008,6 +1010,8 @@
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
mLayer = 0;
+ mOverrideScale = mWmService.mAtmService.mCompatModePackages.getCompatScale(
+ mAttrs.packageName, s.mUid);
// Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged.
@@ -1040,8 +1044,15 @@
mSession.windowAddedLocked(mAttrs.packageName);
}
+ /**
+ * @return {@code true} if the application runs in size compatibility mode or has an app level
+ * scaling override set.
+ * @see CompatModePackages#getCompatScale
+ * @see android.content.res.CompatibilityInfo#supportsScreen
+ * @see ActivityRecord#inSizeCompatMode()
+ */
boolean inSizeCompatMode() {
- return inSizeCompatMode(mAttrs, mActivityRecord);
+ return mOverrideScale != 1f || inSizeCompatMode(mAttrs, mActivityRecord);
}
/**
@@ -1676,7 +1687,13 @@
void prelayout() {
if (inSizeCompatMode()) {
- mGlobalScale = mToken.getSizeCompatScale();
+ if (mOverrideScale != 1f) {
+ mGlobalScale = mToken.hasSizeCompatBounds()
+ ? mToken.getSizeCompatScale() * mOverrideScale
+ : mOverrideScale;
+ } else {
+ mGlobalScale = mToken.getSizeCompatScale();
+ }
mInvGlobalScale = 1 / mGlobalScale;
} else {
mGlobalScale = mInvGlobalScale = 1;
@@ -2634,8 +2651,7 @@
// scaling but the existing logic doesn't expect that. The result is that the already-
// scaled region ends up getting sent to surfaceflinger which then applies the scale
// (again). Until this is resolved, apply an inverse-scale here.
- if (mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
- && mGlobalScale != 1.f) {
+ if (mInvGlobalScale != 1.f) {
region.scale(mInvGlobalScale);
}
@@ -3846,11 +3862,11 @@
}
/**
- * @return {@code true} if bar shown within a given frame is allowed to be transparent
+ * @return {@code true} if bar shown within a given frame is allowed to be fully transparent
* when the current window is displayed.
*/
- boolean isTransparentBarAllowed(Rect frame) {
- return mActivityRecord == null || mActivityRecord.isTransparentBarAllowed(frame);
+ boolean isFullyTransparentBarAllowed(Rect frame) {
+ return mActivityRecord == null || mActivityRecord.isFullyTransparentBarAllowed(frame);
}
public boolean isLetterboxedOverlappingWith(Rect rect) {
@@ -5254,7 +5270,7 @@
if (!mAnimatingExit && mAppDied) {
mIsDimming = true;
getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
- } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || isBlurEnabled())
+ } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || (mAttrs.flags & FLAG_BLUR_BEHIND) != 0)
&& isVisibleNow() && !mHidden) {
// Only show the Dimmer when the following is satisfied:
// 1. The window has the flag FLAG_DIM_BEHIND or background blur is requested
@@ -5263,15 +5279,13 @@
// 4. The WS is not hidden.
mIsDimming = true;
final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
- final int blurRadius = isBlurEnabled() ? mAttrs.backgroundBlurRadius : 0;
- getDimmer().dimBelow(getSyncTransaction(), this, dimAmount, blurRadius);
+ final int blurRadius =
+ (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 ? mAttrs.blurBehindRadius : 0;
+ getDimmer().dimBelow(
+ getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.blurBehindRadius);
}
}
- private boolean isBlurEnabled() {
- return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mOwnerCanUseBackgroundBlur;
- }
-
/**
* Notifies SF about the priority of the window, if it changed. SF then uses this information
* to decide which window's desired rendering rate should have a priority when deciding about
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 995cfe9..9a8942b 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -12,6 +12,9 @@
per-file com_android_server_HardwarePropertiesManagerService.cpp = michaelwr@google.com, santoscordon@google.com
per-file com_android_server_power_PowerManagerService.* = michaelwr@google.com, santoscordon@google.com
+# BatteryStats
+per-file com_android_server_am_BatteryStatsService.cpp = file:/BATTERY_STATS_OWNERS
+
per-file Android.bp = file:platform/build/soong:/OWNERS
per-file com_android_server_Usb* = file:/services/usb/OWNERS
per-file com_android_server_Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index c3e7c7a..16eaa77 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -55,15 +55,8 @@
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::power::stats::V1_0::IPowerStats;
-using android::hardware::power::V1_0::PowerStatePlatformSleepState;
-using android::hardware::power::V1_0::PowerStateVoter;
-using android::hardware::power::V1_0::Status;
-using android::hardware::power::V1_1::PowerStateSubsystem;
-using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
using android::system::suspend::BnSuspendCallback;
using android::system::suspend::ISuspendControlService;
-using IPowerV1_1 = android::hardware::power::V1_1::IPower;
-using IPowerV1_0 = android::hardware::power::V1_0::IPower;
namespace android
{
@@ -74,23 +67,9 @@
static sem_t wakeup_sem;
extern sp<ISuspendControlService> getSuspendControl();
-// Java methods used in getLowPowerStats
-static jmethodID jgetAndUpdatePlatformState = NULL;
-static jmethodID jgetSubsystem = NULL;
-static jmethodID jputVoter = NULL;
-static jmethodID jputState = NULL;
-
std::mutex gPowerStatsHalMutex;
-std::unordered_map<uint32_t, std::string> gPowerStatsHalEntityNames = {};
-std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>>
- gPowerStatsHalStateNames = {};
-std::vector<uint32_t> gPowerStatsHalPlatformIds = {};
-std::vector<uint32_t> gPowerStatsHalSubsystemIds = {};
sp<IPowerStats> gPowerStatsHalV1_0 = nullptr;
-std::function<void(JNIEnv*, jobject)> gGetLowPowerStatsImpl = {};
-std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {};
-std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {};
std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {};
// Cellular/Wifi power monitor rail information
@@ -222,68 +201,14 @@
return true;
}
-static bool checkPowerHalResult(const Return<void>& ret, const char* function) {
- if (!ret.isOk()) {
- ALOGE("%s failed: requested HAL service not available.", function);
- power::PowerHalLoader::unloadAll();
- return false;
- }
- return true;
-}
-
// gPowerStatsHalV1_0 must not be null
static bool initializePowerStatsLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
using android::hardware::power::stats::V1_0::Status;
- using android::hardware::power::stats::V1_0::PowerEntityType;
// Clear out previous content if we are re-initializing
- gPowerStatsHalEntityNames.clear();
- gPowerStatsHalStateNames.clear();
- gPowerStatsHalPlatformIds.clear();
- gPowerStatsHalSubsystemIds.clear();
gPowerStatsHalRailNames.clear();
Return<void> ret;
- ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting power entity info");
- return;
- }
-
- // construct lookup table of powerEntityId to power entity name
- // also construct vector of platform and subsystem IDs
- for (auto info : infos) {
- gPowerStatsHalEntityNames.emplace(info.powerEntityId, info.powerEntityName);
- if (info.type == PowerEntityType::POWER_DOMAIN) {
- gPowerStatsHalPlatformIds.emplace_back(info.powerEntityId);
- } else {
- gPowerStatsHalSubsystemIds.emplace_back(info.powerEntityId);
- }
- }
- });
- if (!checkPowerStatsHalResultLocked(ret, __func__)) {
- return false;
- }
-
- ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting state info");
- return;
- }
-
- // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
- for (auto stateSpace : stateSpaces) {
- std::unordered_map<uint32_t, std::string> stateNames = {};
- for (auto state : stateSpace.states) {
- stateNames.emplace(state.powerEntityStateId,
- state.powerEntityStateName);
- }
- gPowerStatsHalStateNames.emplace(stateSpace.powerEntityId, stateNames);
- }
- });
- if (!checkPowerStatsHalResultLocked(ret, __func__)) {
- return false;
- }
// Get Power monitor rails available
ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) {
@@ -306,7 +231,7 @@
return false;
}
- return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty());
+ return true;
}
static bool getPowerStatsHalLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
@@ -333,196 +258,6 @@
return true;
}
-static void getPowerStatsHalLowPowerDataLocked(JNIEnv* env, jobject jrpmStats)
- EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
- using android::hardware::power::stats::V1_0::Status;
-
- if (!getPowerStatsHalLocked()) {
- ALOGE("failed to get low power stats");
- return;
- }
-
- // Get power entity state residency data
- bool success = false;
- Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
- [&env, &jrpmStats, &success](auto results, auto status) {
- if (status == Status::NOT_SUPPORTED) {
- ALOGW("getPowerEntityStateResidencyData is not supported");
- success = false;
- return;
- }
-
- for (auto result : results) {
- jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
- env->NewStringUTF(gPowerStatsHalEntityNames.at(result.powerEntityId).c_str()));
- if (jsubsystem == NULL) {
- ALOGE("The rpmstats jni jobject jsubsystem is null.");
- return;
- }
- for (auto stateResidency : result.stateResidencyData) {
-
- env->CallVoidMethod(jsubsystem, jputState,
- env->NewStringUTF(gPowerStatsHalStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId).c_str()),
- stateResidency.totalTimeInStateMs,
- stateResidency.totalStateEntryCount);
- }
- }
- success = true;
- });
- checkPowerStatsHalResultLocked(ret, __func__);
- if (!success) {
- ALOGE("getPowerEntityStateResidencyData failed");
- }
-}
-
-static jint getPowerStatsHalPlatformDataLocked(JNIEnv* env, jobject outBuf)
- EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
- using android::hardware::power::stats::V1_0::Status;
- using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
- using hardware::power::stats::V1_0::PowerEntityStateResidencyData;
-
- if (!getPowerStatsHalLocked()) {
- ALOGE("failed to get low power stats");
- return -1;
- }
-
- char *output = (char*)env->GetDirectBufferAddress(outBuf);
- char *offset = output;
- int remaining = (int)env->GetDirectBufferCapacity(outBuf);
- int total_added = -1;
-
- // Get power entity state residency data
- Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
- gPowerStatsHalPlatformIds,
- [&offset, &remaining, &total_added](auto results, auto status) {
- if (status == Status::NOT_SUPPORTED) {
- ALOGW("getPowerEntityStateResidencyData is not supported");
- return;
- }
-
- for (size_t i = 0; i < results.size(); i++) {
- const PowerEntityStateResidencyResult& result = results[i];
-
- for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
- const PowerEntityStateResidencyData& stateResidency =
- result.stateResidencyData[j];
- int added = snprintf(offset, remaining,
- "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
- j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId).c_str(),
- stateResidency.totalTimeInStateMs,
- stateResidency.totalStateEntryCount);
- if (added < 0) {
- break;
- }
- if (added > remaining) {
- added = remaining;
- }
- offset += added;
- remaining -= added;
- total_added += added;
- }
- if (remaining <= 0) {
- /* rewrite NULL character*/
- offset--;
- total_added--;
- ALOGE("power.stats Hal: buffer not enough");
- break;
- }
- }
- });
- if (!checkPowerStatsHalResultLocked(ret, __func__)) {
- return -1;
- }
-
- total_added += 1;
- return total_added;
-}
-
-static jint getPowerStatsHalSubsystemDataLocked(JNIEnv* env, jobject outBuf)
- EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
- using android::hardware::power::stats::V1_0::Status;
- using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
- using hardware::power::stats::V1_0::PowerEntityStateResidencyData;
-
- if (!getPowerStatsHalLocked()) {
- ALOGE("failed to get low power stats");
- return -1;
- }
-
- char *output = (char*)env->GetDirectBufferAddress(outBuf);
- char *offset = output;
- int remaining = (int)env->GetDirectBufferCapacity(outBuf);
- int total_added = -1;
-
- // Get power entity state residency data
- Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
- gPowerStatsHalSubsystemIds,
- [&offset, &remaining, &total_added](auto results, auto status) {
- if (status == Status::NOT_SUPPORTED) {
- ALOGW("getPowerEntityStateResidencyData is not supported");
- return;
- }
-
- int added = snprintf(offset, remaining, "SubsystemPowerState ");
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t i = 0; i < results.size(); i++) {
- const PowerEntityStateResidencyResult& result = results[i];
- added = snprintf(offset, remaining, "subsystem_%zu name=%s ",
- i + 1, gPowerStatsHalEntityNames.at(result.powerEntityId).c_str());
- if (added < 0) {
- break;
- }
-
- if (added > remaining) {
- added = remaining;
- }
-
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
- const PowerEntityStateResidencyData& stateResidency =
- result.stateResidencyData[j];
- added = snprintf(offset, remaining,
- "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%"
- PRIu64 " ", j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId).c_str(),
- stateResidency.totalTimeInStateMs,
- stateResidency.totalStateEntryCount,
- stateResidency.lastEntryTimestampMs);
- if (added < 0) {
- break;
- }
- if (added > remaining) {
- added = remaining;
- }
- offset += added;
- remaining -= added;
- total_added += added;
- }
- if (remaining <= 0) {
- /* rewrite NULL character*/
- offset--;
- total_added--;
- ALOGE("power.stats Hal: buffer not enough");
- break;
- }
- }
- });
- if (!checkPowerStatsHalResultLocked(ret, __func__)) {
- return -1;
- }
-
- total_added += 1;
- return total_added;
-}
-
static void getPowerStatsHalRailEnergyDataLocked(JNIEnv* env, jobject jrailStats)
EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
using android::hardware::power::stats::V1_0::Status;
@@ -568,325 +303,17 @@
}
}
-static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
- sp<IPowerV1_0> powerHalV1_0 = power::PowerHalLoader::loadHidlV1_0();
- if (powerHalV1_0 == nullptr) {
- ALOGE("Power Hal not loaded");
- return;
- }
-
- Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
- [&env, &jrpmStats](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
-
- if (status != Status::SUCCESS) return;
-
- for (size_t i = 0; i < states.size(); i++) {
- const PowerStatePlatformSleepState& state = states[i];
-
- jobject jplatformState = env->CallObjectMethod(jrpmStats,
- jgetAndUpdatePlatformState,
- env->NewStringUTF(state.name.c_str()),
- state.residencyInMsecSinceBoot,
- state.totalTransitions);
- if (jplatformState == NULL) {
- ALOGE("The rpmstats jni jobject jplatformState is null.");
- return;
- }
-
- for (size_t j = 0; j < state.voters.size(); j++) {
- const PowerStateVoter& voter = state.voters[j];
- env->CallVoidMethod(jplatformState, jputVoter,
- env->NewStringUTF(voter.name.c_str()),
- voter.totalTimeInMsecVotedForSinceBoot,
- voter.totalNumberOfTimesVotedSinceBoot);
- }
- }
- });
- if (!checkPowerHalResult(ret, "getPlatformLowPowerStats")) {
- return;
- }
-
- // Trying to get IPower 1.1, this will succeed only for devices supporting 1.1
- sp<IPowerV1_1> powerHal_1_1 = power::PowerHalLoader::loadHidlV1_1();
- if (powerHal_1_1 == nullptr) {
- // This device does not support IPower@1.1, exiting gracefully
- return;
- }
- ret = powerHal_1_1->getSubsystemLowPowerStats(
- [&env, &jrpmStats](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
-
- if (status != Status::SUCCESS) return;
-
- if (subsystems.size() > 0) {
- for (size_t i = 0; i < subsystems.size(); i++) {
- const PowerStateSubsystem &subsystem = subsystems[i];
-
- jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
- env->NewStringUTF(subsystem.name.c_str()));
- if (jsubsystem == NULL) {
- ALOGE("The rpmstats jni jobject jsubsystem is null.");
- return;
- }
-
- for (size_t j = 0; j < subsystem.states.size(); j++) {
- const PowerStateSubsystemSleepState& state = subsystem.states[j];
- env->CallVoidMethod(jsubsystem, jputState,
- env->NewStringUTF(state.name.c_str()),
- state.residencyInMsecSinceBoot,
- state.totalTransitions);
- }
- }
- }
- });
- checkPowerHalResult(ret, "getSubsystemLowPowerStats");
-}
-
-static jint getPowerHalPlatformData(JNIEnv* env, jobject outBuf) {
- char *output = (char*)env->GetDirectBufferAddress(outBuf);
- char *offset = output;
- int remaining = (int)env->GetDirectBufferCapacity(outBuf);
- int total_added = -1;
-
- {
- sp<IPowerV1_0> powerHalV1_0 = power::PowerHalLoader::loadHidlV1_0();
- if (powerHalV1_0 == nullptr) {
- ALOGE("Power Hal not loaded");
- return -1;
- }
-
- Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
- [&offset, &remaining, &total_added](hidl_vec<PowerStatePlatformSleepState> states,
- Status status) {
- if (status != Status::SUCCESS)
- return;
- for (size_t i = 0; i < states.size(); i++) {
- int added;
- const PowerStatePlatformSleepState& state = states[i];
-
- added = snprintf(offset, remaining,
- "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
- i + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
- state.totalTransitions);
- if (added < 0) {
- break;
- }
- if (added > remaining) {
- added = remaining;
- }
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t j = 0; j < state.voters.size(); j++) {
- const PowerStateVoter& voter = state.voters[j];
- added = snprintf(offset, remaining,
- "voter_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
- j + 1, voter.name.c_str(),
- voter.totalTimeInMsecVotedForSinceBoot,
- voter.totalNumberOfTimesVotedSinceBoot);
- if (added < 0) {
- break;
- }
- if (added > remaining) {
- added = remaining;
- }
- offset += added;
- remaining -= added;
- total_added += added;
- }
-
- if (remaining <= 0) {
- /* rewrite NULL character*/
- offset--;
- total_added--;
- ALOGE("PowerHal: buffer not enough");
- break;
- }
- }
- }
- );
-
- if (!checkPowerHalResult(ret, "getPlatformLowPowerStats")) {
- return -1;
- }
- }
- *offset = 0;
- total_added += 1;
- return total_added;
-}
-
-static jint getPowerHalSubsystemData(JNIEnv* env, jobject outBuf) {
- char *output = (char*)env->GetDirectBufferAddress(outBuf);
- char *offset = output;
- int remaining = (int)env->GetDirectBufferCapacity(outBuf);
- int total_added = -1;
-
- // This is a IPower 1.1 API
- sp<IPowerV1_1> powerHal_1_1 = nullptr;
-
- {
- // Trying to get 1.1, this will succeed only for devices supporting 1.1
- powerHal_1_1 = power::PowerHalLoader::loadHidlV1_1();
- if (powerHal_1_1 == nullptr) {
- //This device does not support IPower@1.1, exiting gracefully
- return 0;
- }
-
- Return<void> ret = powerHal_1_1->getSubsystemLowPowerStats(
- [&offset, &remaining, &total_added](hidl_vec<PowerStateSubsystem> subsystems,
- Status status) {
-
- if (status != Status::SUCCESS)
- return;
-
- if (subsystems.size() > 0) {
- int added = snprintf(offset, remaining, "SubsystemPowerState ");
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t i = 0; i < subsystems.size(); i++) {
- const PowerStateSubsystem &subsystem = subsystems[i];
-
- added = snprintf(offset, remaining,
- "subsystem_%zu name=%s ", i + 1, subsystem.name.c_str());
- if (added < 0) {
- break;
- }
-
- if (added > remaining) {
- added = remaining;
- }
-
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t j = 0; j < subsystem.states.size(); j++) {
- const PowerStateSubsystemSleepState& state = subsystem.states[j];
- added = snprintf(offset, remaining,
- "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%" PRIu64 " ",
- j + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
- state.totalTransitions, state.lastEntryTimestampMs);
- if (added < 0) {
- break;
- }
-
- if (added > remaining) {
- added = remaining;
- }
-
- offset += added;
- remaining -= added;
- total_added += added;
- }
-
- if (remaining <= 0) {
- /* rewrite NULL character*/
- offset--;
- total_added--;
- ALOGE("PowerHal: buffer not enough");
- break;
- }
- }
- }
- }
- );
-
- if (!checkPowerHalResult(ret, "getSubsystemLowPowerStats")) {
- return -1;
- }
- }
-
- *offset = 0;
- total_added += 1;
- return total_added;
-}
-
static void setUpPowerStatsLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
// First see if power.stats HAL is available. Fall back to power HAL if
// power.stats HAL is unavailable.
if (IPowerStats::getService() != nullptr) {
ALOGI("Using power.stats HAL");
- gGetLowPowerStatsImpl = getPowerStatsHalLowPowerDataLocked;
- gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformDataLocked;
- gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemDataLocked;
gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyDataLocked;
- } else if (IPowerV1_0::getService() != nullptr) {
- ALOGI("Using power HAL");
- gGetLowPowerStatsImpl = getPowerHalLowPowerData;
- gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData;
- gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData;
+ } else {
gGetRailEnergyPowerStatsImpl = NULL;
}
}
-static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats) {
- if (jrpmStats == NULL) {
- jniThrowException(env, "java/lang/NullPointerException",
- "The rpmstats jni input jobject jrpmStats is null.");
- return;
- }
- if (jgetAndUpdatePlatformState == NULL || jgetSubsystem == NULL
- || jputVoter == NULL || jputState == NULL) {
- ALOGE("A rpmstats jni jmethodID is null.");
- return;
- }
-
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!gGetLowPowerStatsImpl) {
- setUpPowerStatsLocked();
- }
-
- if (gGetLowPowerStatsImpl) {
- return gGetLowPowerStatsImpl(env, jrpmStats);
- }
-
- ALOGE("Unable to load Power Hal or power.stats HAL");
- return;
-}
-
-static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
- if (outBuf == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "null argument");
- return -1;
- }
-
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!gGetPlatformLowPowerStatsImpl) {
- setUpPowerStatsLocked();
- }
-
- if (gGetPlatformLowPowerStatsImpl) {
- return gGetPlatformLowPowerStatsImpl(env, outBuf);
- }
-
- ALOGE("Unable to load Power Hal or power.stats HAL");
- return -1;
-}
-
-static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
- if (outBuf == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "null argument");
- return -1;
- }
-
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!gGetSubsystemLowPowerStatsImpl) {
- setUpPowerStatsLocked();
- }
-
- if (gGetSubsystemLowPowerStatsImpl) {
- return gGetSubsystemLowPowerStatsImpl(env, outBuf);
- }
-
- ALOGE("Unable to load Power Hal or power.stats HAL");
- return -1;
-}
-
static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) {
if (jrailStats == NULL) {
jniThrowException(env, "java/lang/NullPointerException",
@@ -920,9 +347,6 @@
static const JNINativeMethod method_table[] = {
{ "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
- { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },
- { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
- { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats },
{ "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V",
(void*)getRailEnergyPowerStats },
};
@@ -930,24 +354,10 @@
int register_android_server_BatteryStatsService(JNIEnv *env)
{
// get java classes and methods
- jclass clsRpmStats = env->FindClass("com/android/internal/os/RpmStats");
- jclass clsPowerStatePlatformSleepState =
- env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState");
- jclass clsPowerStateSubsystem =
- env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem");
jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats");
- if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL
- || clsPowerStateSubsystem == NULL || clsRailStats == NULL) {
+ if (clsRailStats == NULL) {
ALOGE("A rpmstats jni jclass is null.");
} else {
- jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState",
- "(Ljava/lang/String;JI)Lcom/android/internal/os/RpmStats$PowerStatePlatformSleepState;");
- jgetSubsystem = env->GetMethodID(clsRpmStats, "getSubsystem",
- "(Ljava/lang/String;)Lcom/android/internal/os/RpmStats$PowerStateSubsystem;");
- jputVoter = env->GetMethodID(clsPowerStatePlatformSleepState, "putVoter",
- "(Ljava/lang/String;JI)V");
- jputState = env->GetMethodID(clsPowerStateSubsystem, "putState",
- "(Ljava/lang/String;JI)V");
jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData",
"(JLjava/lang/String;Ljava/lang/String;JJ)V");
jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability",
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 156ef79..31cc295 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -123,7 +123,7 @@
}
return -1;
}
-static bool getAnyPageAdvice(const Vma& vma) {
+static int getAnyPageAdvice(const Vma& vma) {
if (vma.inode == 0 && !vma.is_shared) {
return MADV_PAGEOUT;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index dc15b07..5b587e9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -26,14 +26,14 @@
// Log debug messages about InputDispatcherPolicy
#define DEBUG_INPUT_DISPATCHER_POLICY 0
-
-#include <atomic>
-#include <cinttypes>
-#include <limits.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
+#include <android/os/IInputConstants.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
+#include <limits.h>
+#include <atomic>
+#include <cinttypes>
#include <utils/Log.h>
#include <utils/Looper.h>
@@ -46,6 +46,7 @@
#include <input/SpriteController.h>
#include <ui/Region.h>
+#include <batteryservice/include/batteryservice/BatteryServiceConstants.h>
#include <inputflinger/InputManager.h>
#include <android_os_MessageQueue.h>
@@ -1908,6 +1909,20 @@
return vibIdArray;
}
+static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryCapacity(deviceId);
+ return static_cast<jint>(ret.value_or(android::os::IInputConstants::INVALID_BATTERY_CAPACITY));
+}
+
+static jint nativeGetBatteryStatus(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryStatus(deviceId);
+ return static_cast<jint>(ret.value_or(BATTERY_STATUS_UNKNOWN));
+}
+
static void nativeReloadKeyboardLayouts(JNIEnv* /* env */,
jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -2163,6 +2178,8 @@
{"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate},
{"nativeIsVibrating", "(JI)Z", (void*)nativeIsVibrating},
{"nativeGetVibratorIds", "(JI)[I", (void*)nativeGetVibratorIds},
+ {"nativeGetBatteryCapacity", "(JI)I", (void*)nativeGetBatteryCapacity},
+ {"nativeGetBatteryStatus", "(JI)I", (void*)nativeGetBatteryStatus},
{"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts},
{"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases},
{"nativeDump", "(J)Ljava/lang/String;", (void*)nativeDump},
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 7b379e5..5a5b0a8 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -22,6 +22,7 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <core_jni_helpers.h>
+#include <cutils/multiuser.h>
#include <cutils/trace.h>
#include <endian.h>
#include <nativehelper/JNIHelp.h>
@@ -375,6 +376,9 @@
}
private:
+ // Bitmask of supported features.
+ DataLoaderFeatures getFeatures() const final { return DATA_LOADER_FEATURE_UID; }
+
// Lifecycle.
bool onCreate(const android::dataloader::DataLoaderParams& params,
android::dataloader::FilesystemConnectorPtr ifs,
@@ -554,51 +558,6 @@
return true;
}
- // Read tracing.
- struct TracedRead {
- uint64_t timestampUs;
- android::dataloader::FileId fileId;
- uint32_t firstBlockIdx;
- uint32_t count;
- };
-
- void onPageReads(android::dataloader::PageReads pageReads) final {
- auto trace = atrace_is_tag_enabled(ATRACE_TAG);
- if (CC_LIKELY(!trace)) {
- return;
- }
-
- TracedRead last = {};
- for (auto&& read : pageReads) {
- if (read.id != last.fileId || read.block != last.firstBlockIdx + last.count) {
- traceRead(last);
- last = TracedRead{
- .timestampUs = read.bootClockTsUs,
- .fileId = read.id,
- .firstBlockIdx = (uint32_t)read.block,
- .count = 1,
- };
- } else {
- ++last.count;
- }
- }
- traceRead(last);
- }
-
- void traceRead(const TracedRead& read) {
- if (!read.count) {
- return;
- }
-
- FileIdx fileIdx = convertFileIdToFileIndex(read.fileId);
- auto str = android::base::StringPrintf("page_read: index=%lld count=%lld file=%d",
- static_cast<long long>(read.firstBlockIdx),
- static_cast<long long>(read.count),
- static_cast<int>(fileIdx));
- ATRACE_BEGIN(str.c_str());
- ATRACE_END();
- }
-
// Streaming.
bool initStreaming(unique_fd inout, MetadataMode mode) {
mEventFd.reset(eventfd(0, EFD_CLOEXEC));
@@ -634,7 +593,10 @@
}
// IFS callbacks.
- void onPendingReads(dataloader::PendingReads pendingReads) final {
+ void onPendingReads(dataloader::PendingReads pendingReads) final {}
+ void onPageReads(dataloader::PageReads pageReads) final {}
+
+ void onPendingReadsWithUid(dataloader::PendingReadsWithUid pendingReads) final {
std::lock_guard lock{mOutFdLock};
if (mOutFd < 0) {
return;
@@ -660,6 +622,67 @@
}
}
+ // Read tracing.
+ struct TracedRead {
+ uint64_t timestampUs;
+ android::dataloader::FileId fileId;
+ android::dataloader::Uid uid;
+ uint32_t firstBlockIdx;
+ uint32_t count;
+ };
+
+ void onPageReadsWithUid(dataloader::PageReadsWithUid pageReads) final {
+ auto trace = atrace_is_tag_enabled(ATRACE_TAG);
+ if (CC_LIKELY(!trace)) {
+ return;
+ }
+
+ TracedRead last = {};
+ for (auto&& read : pageReads) {
+ if (read.id != last.fileId || read.uid != last.uid ||
+ read.block != last.firstBlockIdx + last.count) {
+ traceRead(last);
+ last = TracedRead{
+ .timestampUs = read.bootClockTsUs,
+ .fileId = read.id,
+ .uid = read.uid,
+ .firstBlockIdx = (uint32_t)read.block,
+ .count = 1,
+ };
+ } else {
+ ++last.count;
+ }
+ }
+ traceRead(last);
+ }
+
+ void traceRead(const TracedRead& read) {
+ if (!read.count) {
+ return;
+ }
+
+ FileIdx fileIdx = convertFileIdToFileIndex(read.fileId);
+
+ std::string trace;
+ if (read.uid != kIncFsNoUid) {
+ auto appId = multiuser_get_app_id(read.uid);
+ auto userId = multiuser_get_user_id(read.uid);
+ trace = android::base::
+ StringPrintf("page_read: index=%lld count=%lld file=%d appid=%d userid=%d",
+ static_cast<long long>(read.firstBlockIdx),
+ static_cast<long long>(read.count), static_cast<int>(fileIdx),
+ static_cast<int>(appId), static_cast<int>(userId));
+ } else {
+ trace = android::base::StringPrintf("page_read: index=%lld count=%lld file=%d",
+ static_cast<long long>(read.firstBlockIdx),
+ static_cast<long long>(read.count),
+ static_cast<int>(fileIdx));
+ }
+
+ ATRACE_BEGIN(trace.c_str());
+ ATRACE_END();
+ }
+
void receiver(unique_fd inout, MetadataMode mode) {
std::vector<uint8_t> data;
std::vector<IncFsDataBlock> instructions;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 1194099..11e4db5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -120,11 +120,11 @@
@NonNull String callerPackage, @NonNull String enterpriseId, int userId) {}
public UserHandle createAndProvisionManagedProfile(
- @NonNull ManagedProfileProvisioningParams provisioningParams) {
+ @NonNull ManagedProfileProvisioningParams provisioningParams, String callerPackage) {
return null;
}
public void provisionFullyManagedDevice(
- FullyManagedDeviceProvisioningParams provisioningParams) {
+ FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 15bc93e..8b2beb2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -43,10 +43,14 @@
@GuardedBy("mLock")
private final SparseIntArray mPasswordQuality = new SparseIntArray();
+ @GuardedBy("mLock")
+ private final SparseIntArray mPermissionPolicy = new SparseIntArray();
+
public void onUserRemoved(int userHandle) {
synchronized (mLock) {
mScreenCaptureDisabled.delete(userHandle);
mPasswordQuality.delete(userHandle);
+ mPermissionPolicy.delete(userHandle);
}
}
@@ -78,12 +82,28 @@
}
}
+ @Override
+ public int getPermissionPolicy(@UserIdInt int userHandle) {
+ synchronized (mLock) {
+ return mPermissionPolicy.get(userHandle,
+ DevicePolicyManager.PERMISSION_POLICY_PROMPT);
+ }
+ }
+
+ /** Update the permission policy for the given user. */
+ public void setPermissionPolicy(@UserIdInt int userHandle, int policy) {
+ synchronized (mLock) {
+ mPermissionPolicy.put(userHandle, policy);
+ }
+ }
+
/** Dump content */
public void dump(IndentingPrintWriter pw) {
pw.println("Device policy cache:");
pw.increaseIndent();
pw.println("Screen capture disabled: " + mScreenCaptureDisabled.toString());
pw.println("Password quality: " + mPasswordQuality.toString());
+ pw.println("Permission policy: " + mPermissionPolicy.toString());
pw.decreaseIndent();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c3bb757..3d2e5de 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -35,10 +35,8 @@
import static android.app.admin.DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.CODE_NONSYSTEM_USER_EXISTS;
import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER;
-import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER_SPLIT;
import static android.app.admin.DevicePolicyManager.CODE_OK;
import static android.app.admin.DevicePolicyManager.CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
-import static android.app.admin.DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
import static android.app.admin.DevicePolicyManager.CODE_SYSTEM_USER;
import static android.app.admin.DevicePolicyManager.CODE_USER_HAS_PROFILE_OWNER;
import static android.app.admin.DevicePolicyManager.CODE_USER_NOT_RUNNING;
@@ -94,6 +92,7 @@
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
+import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -130,6 +129,7 @@
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -159,6 +159,7 @@
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.DeviceStateCache;
@@ -560,6 +561,21 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long USE_SET_LOCATION_ENABLED = 117835097L;
+ // Only add to the end of the list. Do not change or rearrange these values, that will break
+ // historical data. Do not use negative numbers or zero, logger only handles positive
+ // integers.
+ private static final int COPY_ACCOUNT_SUCCEEDED = 1;
+ private static final int COPY_ACCOUNT_FAILED = 2;
+ private static final int COPY_ACCOUNT_TIMED_OUT = 3;
+ private static final int COPY_ACCOUNT_EXCEPTION = 4;
+
+ @IntDef({
+ COPY_ACCOUNT_SUCCEEDED,
+ COPY_ACCOUNT_FAILED,
+ COPY_ACCOUNT_TIMED_OUT,
+ COPY_ACCOUNT_EXCEPTION})
+ private @interface CopyAccountStatus {}
+
/**
* Admin apps targeting Android S+ may not use
* {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality
@@ -1069,30 +1085,35 @@
* @throws UnsafeStateException if it's not safe to execute the operation.
*/
private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) {
- if (!canExecute(operation)) {
- if (mSafetyChecker == null) {
- // Happens on CTS after it's set just once (by OneTimeSafetyChecker)
- throw new UnsafeStateException(operation);
- }
- // Let mSafetyChecker customize it (for example, by explaining how to retry)
- throw mSafetyChecker.newUnsafeStateException(operation);
+ int reason = getUnsafeOperationReason(operation);
+ if (reason == UNSAFE_OPERATION_REASON_NONE) return;
+
+ if (mSafetyChecker == null) {
+ // Happens on CTS after it's set just once (by OneTimeSafetyChecker)
+ throw new UnsafeStateException(operation, reason);
}
+ // Let mSafetyChecker customize it (for example, by explaining how to retry)
+ throw mSafetyChecker.newUnsafeStateException(operation, reason);
}
/**
- * Returns whether it's safe to execute the given {@code operation}.
+ * Returns whether it's safe to execute the given {@code operation}, and why.
*/
- boolean canExecute(@DevicePolicyOperation int operation) {
- return mSafetyChecker == null || mSafetyChecker.isDevicePolicyOperationSafe(operation);
+ @UnsafeOperationReason
+ int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
+ return mSafetyChecker == null ? UNSAFE_OPERATION_REASON_NONE
+ : mSafetyChecker.getUnsafeOperationReason(operation);
}
@Override
- public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) {
+ public void setNextOperationSafety(@DevicePolicyOperation int operation,
+ @UnsafeOperationReason int reason) {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
- Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %b)",
- DevicePolicyManager.operationToString(operation), safe));
- mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe);
+ Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %s)",
+ DevicePolicyManager.operationToString(operation),
+ DevicePolicyManager.unsafeOperationReasonToString(reason)));
+ mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
}
/**
@@ -1380,11 +1401,6 @@
SystemProperties.set(key, value);
}
- // TODO (b/137101239): clean up split system user codes
- boolean userManagerIsSplitSystemUser() {
- return UserManager.isSplitSystemUser();
- }
-
boolean userManagerIsHeadlessSystemUserMode() {
return UserManager.isHeadlessSystemUserMode();
}
@@ -2942,6 +2958,7 @@
// reading the value during user switch, due to onStartUser() being asynchronous.
updatePasswordQualityCacheForUserGroup(
userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId);
+ updatePermissionPolicyCache(userId);
startOwnerService(userId, "start-user");
}
@@ -7404,48 +7421,18 @@
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
}
+ // TODO (b/137101239): remove this method in follow-up CL
+ // since it's only used for split system user.
@Override
public void setForceEphemeralUsers(ComponentName who, boolean forceEphemeralUsers) {
- if (!mHasFeature) {
- return;
- }
- Objects.requireNonNull(who, "ComponentName is null");
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
-
- // Allow setting this policy to true only if there is a split system user.
- if (forceEphemeralUsers && !mInjector.userManagerIsSplitSystemUser()) {
- throw new UnsupportedOperationException(
- "Cannot force ephemeral users on systems without split system user.");
- }
- boolean removeAllUsers = false;
- synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- if (deviceOwner.forceEphemeralUsers != forceEphemeralUsers) {
- deviceOwner.forceEphemeralUsers = forceEphemeralUsers;
- saveSettingsLocked(caller.getUserId());
- mUserManagerInternal.setForceEphemeralUsers(forceEphemeralUsers);
- removeAllUsers = forceEphemeralUsers;
- }
- }
- if (removeAllUsers) {
- mInjector.binderWithCleanCallingIdentity(() -> mUserManagerInternal.removeAllUsers());
- }
+ throw new UnsupportedOperationException("This method was used by split system user only.");
}
+ // TODO (b/137101239): remove this method in follow-up CL
+ // since it's only used for split system user.
@Override
public boolean getForceEphemeralUsers(ComponentName who) {
- if (!mHasFeature) {
- return false;
- }
- Objects.requireNonNull(who, "ComponentName is null");
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
-
- synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- return deviceOwner.forceEphemeralUsers;
- }
+ throw new UnsupportedOperationException("This method was used by split system user only.");
}
@Override
@@ -12498,11 +12485,13 @@
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_POLICY);
+ final int forUser = caller.getUserId();
synchronized (getLockObject()) {
- DevicePolicyData userPolicy = getUserData(caller.getUserId());
+ DevicePolicyData userPolicy = getUserData(forUser);
if (userPolicy.mPermissionPolicy != policy) {
userPolicy.mPermissionPolicy = policy;
- saveSettingsLocked(caller.getUserId());
+ mPolicyCache.setPermissionPolicy(forUser, policy);
+ saveSettingsLocked(forUser);
}
}
DevicePolicyEventLogger
@@ -12513,13 +12502,17 @@
.write();
}
+ private void updatePermissionPolicyCache(int userId) {
+ synchronized (getLockObject()) {
+ DevicePolicyData userPolicy = getUserData(userId);
+ mPolicyCache.setPermissionPolicy(userId, userPolicy.mPermissionPolicy);
+ }
+ }
+
@Override
public int getPermissionPolicy(ComponentName admin) throws RemoteException {
int userId = UserHandle.getCallingUserId();
- synchronized (getLockObject()) {
- DevicePolicyData userPolicy = getUserData(userId);
- return userPolicy.mPermissionPolicy;
- }
+ return mPolicyCache.getPermissionPolicy(userId);
}
@Override
@@ -12733,13 +12726,6 @@
case DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE:
case DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE:
return checkDeviceOwnerProvisioningPreCondition(callingUserId);
- // TODO (b/137101239): clean up split system user codes
- // ACTION_PROVISION_MANAGED_USER and ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE
- // only supported on split-user systems.
- case DevicePolicyManager.ACTION_PROVISION_MANAGED_USER:
- return checkManagedUserProvisioningPreCondition(callingUserId);
- case DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE:
- return checkManagedShareableDeviceProvisioningPreCondition(callingUserId);
}
}
throw new IllegalArgumentException("Unknown provisioning action " + action);
@@ -12775,14 +12761,12 @@
}
}
- // TODO (b/137101239): clean up split system user codes
if (isAdb) {
// If shell command runs after user setup completed check device status. Otherwise, OK.
if (mIsWatch || hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
// In non-headless system user mode, DO can be setup only if
// there's no non-system user
if (!mInjector.userManagerIsHeadlessSystemUserMode()
- && !mInjector.userManagerIsSplitSystemUser()
&& mUserManager.getUserCount() > 1) {
return CODE_NONSYSTEM_USER_EXISTS;
}
@@ -12801,16 +12785,13 @@
}
return CODE_OK;
} else {
- if (!mInjector.userManagerIsSplitSystemUser()) {
- // In non-split user mode, DO has to be user 0
- if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
- return CODE_NOT_SYSTEM_USER;
- }
- // Only provision DO before setup wizard completes
- // TODO (b/171423186): implement deferred DO setup for headless system user mode
- if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
- return CODE_USER_SETUP_COMPLETED;
- }
+ // DO has to be user 0
+ if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
+ return CODE_NOT_SYSTEM_USER;
+ }
+ // Only provision DO before setup wizard completes
+ if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
+ return CODE_USER_SETUP_COMPLETED;
}
return CODE_OK;
}
@@ -12831,17 +12812,11 @@
}
}
- // TODO (b/137101239): clean up split system user codes
private int checkManagedProfileProvisioningPreCondition(String packageName,
@UserIdInt int callingUserId) {
if (!hasFeatureManagedUsers()) {
return CODE_MANAGED_USERS_NOT_SUPPORTED;
}
- if (callingUserId == UserHandle.USER_SYSTEM
- && mInjector.userManagerIsSplitSystemUser()) {
- // Managed-profiles cannot be setup on the system user.
- return CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
- }
if (getProfileOwnerAsUser(callingUserId) != null) {
// Managed user cannot have a managed profile.
return CODE_USER_HAS_PROFILE_OWNER;
@@ -12917,37 +12892,6 @@
return null;
}
- // TODO (b/137101239): clean up split system user codes
- private int checkManagedUserProvisioningPreCondition(int callingUserId) {
- if (!hasFeatureManagedUsers()) {
- return CODE_MANAGED_USERS_NOT_SUPPORTED;
- }
- if (!mInjector.userManagerIsSplitSystemUser()) {
- // ACTION_PROVISION_MANAGED_USER only supported on split-user systems.
- return CODE_NOT_SYSTEM_USER_SPLIT;
- }
- if (callingUserId == UserHandle.USER_SYSTEM) {
- // System user cannot be a managed user.
- return CODE_SYSTEM_USER;
- }
- if (hasUserSetupCompleted(callingUserId)) {
- return CODE_USER_SETUP_COMPLETED;
- }
- if (mIsWatch && hasPaired(UserHandle.USER_SYSTEM)) {
- return CODE_HAS_PAIRED;
- }
- return CODE_OK;
- }
-
- // TODO (b/137101239): clean up split system user codes
- private int checkManagedShareableDeviceProvisioningPreCondition(int callingUserId) {
- if (!mInjector.userManagerIsSplitSystemUser()) {
- // ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE only supported on split-user systems.
- return CODE_NOT_SYSTEM_USER_SPLIT;
- }
- return checkDeviceOwnerProvisioningPreCondition(callingUserId);
- }
-
private boolean hasFeatureManagedUsers() {
try {
return mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0);
@@ -16006,11 +15950,12 @@
@Override
public UserHandle createAndProvisionManagedProfile(
- @NonNull ManagedProfileProvisioningParams provisioningParams) {
+ @NonNull ManagedProfileProvisioningParams provisioningParams,
+ @NonNull String callerPackage) {
final ComponentName admin = provisioningParams.getProfileAdminComponentName();
Objects.requireNonNull(admin, "admin is null");
- final CallerIdentity caller = getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentity(callerPackage);
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
@@ -16025,6 +15970,7 @@
"Provisioning preconditions failed with result: " + result);
}
+ final long startTime = SystemClock.elapsedRealtime();
final Set<String> nonRequiredApps = provisioningParams.isLeaveAllSystemAppsEnabled()
? Collections.emptySet()
: mOverlayPackagesProvider.getNonRequiredApps(
@@ -16041,8 +15987,12 @@
"Error creating profile, createProfileForUserEvenWhenDisallowed "
+ "returned null.");
}
-
resetInteractAcrossProfilesAppOps();
+ logEventDuration(
+ DevicePolicyEnums.PLATFORM_PROVISIONING_CREATE_PROFILE_MS,
+ startTime,
+ callerPackage);
+
installExistingAdminPackage(userInfo.id, admin.getPackageName());
if (!enableAdminAndSetProfileOwner(
userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
@@ -16052,10 +16002,10 @@
}
setUserSetupComplete(userInfo.id);
- startUser(userInfo.id);
+ startUser(userInfo.id, callerPackage);
maybeMigrateAccount(
userInfo.id, caller.getUserId(), provisioningParams.getAccountToMigrate(),
- provisioningParams.isKeepAccountMigrated());
+ provisioningParams.isKeepAccountMigrated(), callerPackage);
if (provisioningParams.isOrganizationOwnedProvisioning()) {
markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id);
@@ -16073,7 +16023,11 @@
return userInfo.getUserHandle();
} catch (Exception e) {
- // in case of any errors during provisioning, remove the newly created profile.
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
+ .setStrings(callerPackage)
+ .write();
+ // In case of any errors during provisioning, remove the newly created profile.
if (userInfo != null) {
mUserManager.removeUserEvenWhenDisallowed(userInfo.id);
}
@@ -16178,7 +16132,9 @@
mContext.getContentResolver(), USER_SETUP_COMPLETE, 1, userId);
}
- private void startUser(@UserIdInt int userId) throws IllegalStateException {
+ private void startUser(@UserIdInt int userId, String callerPackage)
+ throws IllegalStateException {
+ final long startTime = SystemClock.elapsedRealtime();
final UserUnlockedBlockingReceiver unlockedReceiver = new UserUnlockedBlockingReceiver(
userId);
mContext.registerReceiverAsUser(
@@ -16197,6 +16153,10 @@
throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
String.format("Timeout whilst waiting for unlock of user %d.", userId));
}
+ logEventDuration(
+ DevicePolicyEnums.PLATFORM_PROVISIONING_START_PROFILE_MS,
+ startTime,
+ callerPackage);
} catch (RemoteException e) {
// Shouldn't happen.
} finally {
@@ -16204,9 +16164,9 @@
}
}
- void maybeMigrateAccount(
+ private void maybeMigrateAccount(
@UserIdInt int targetUserId, @UserIdInt int sourceUserId, Account accountToMigrate,
- boolean keepAccountMigrated) {
+ boolean keepAccountMigrated, String callerPackage) {
final UserHandle sourceUser = UserHandle.of(sourceUserId);
final UserHandle targetUser = UserHandle.of(targetUserId);
if (accountToMigrate == null) {
@@ -16217,13 +16177,16 @@
Slog.w(LOG_TAG, "sourceUser and targetUser are the same, won't migrate account.");
return;
}
- copyAccount(targetUser, sourceUser, accountToMigrate);
+ copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
if (!keepAccountMigrated) {
removeAccount(accountToMigrate);
}
}
- void copyAccount(UserHandle targetUser, UserHandle sourceUser, Account accountToMigrate) {
+ private void copyAccount(
+ UserHandle targetUser, UserHandle sourceUser, Account accountToMigrate,
+ String callerPackage) {
+ final long startTime = SystemClock.elapsedRealtime();
try {
final AccountManager accountManager = mContext.getSystemService(AccountManager.class);
final boolean copySucceeded = accountManager.copyAccountToUser(
@@ -16232,16 +16195,35 @@
targetUser,
/* callback= */ null, /* handler= */ null)
.getResult(60 * 3, TimeUnit.SECONDS);
- if (!copySucceeded) {
+ if (copySucceeded) {
+ logCopyAccountStatus(COPY_ACCOUNT_SUCCEEDED, callerPackage);
+ logEventDuration(
+ DevicePolicyEnums.PLATFORM_PROVISIONING_COPY_ACCOUNT_MS,
+ startTime,
+ callerPackage);
+ } else {
+ logCopyAccountStatus(COPY_ACCOUNT_FAILED, callerPackage);
Slog.e(LOG_TAG, "Failed to copy account to " + targetUser);
}
- } catch (OperationCanceledException | AuthenticatorException | IOException e) {
+ } catch (OperationCanceledException e) {
// Account migration is not considered a critical operation.
+ logCopyAccountStatus(COPY_ACCOUNT_TIMED_OUT, callerPackage);
+ Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
+ } catch (AuthenticatorException | IOException e) {
+ logCopyAccountStatus(COPY_ACCOUNT_EXCEPTION, callerPackage);
Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
}
}
- void removeAccount(Account account) {
+ private static void logCopyAccountStatus(@CopyAccountStatus int status, String callerPackage) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_COPY_ACCOUNT_STATUS)
+ .setInt(status)
+ .setStrings(callerPackage)
+ .write();
+ }
+
+ private void removeAccount(Account account) {
final AccountManager accountManager =
mContext.getSystemService(AccountManager.class);
final AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account,
@@ -16287,7 +16269,7 @@
@Override
public void provisionFullyManagedDevice(
- FullyManagedDeviceProvisioningParams provisioningParams) {
+ FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
ComponentName deviceAdmin = provisioningParams.getDeviceAdminComponentName();
Objects.requireNonNull(deviceAdmin, "admin is null.");
@@ -16299,12 +16281,16 @@
final long identity = Binder.clearCallingIdentity();
try {
- int result = checkProvisioningPreConditionSkipPermission(
- ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
- if (result != CODE_OK) {
- throw new ServiceSpecificException(
- PROVISIONING_RESULT_PRE_CONDITION_FAILED,
- "Provisioning preconditions failed with result: " + result);
+ // TODO(b/178187130): This check fails silent provisioning, uncomment once silent
+ // provisioning is no longer used.
+ if (false) {
+ int result = checkProvisioningPreConditionSkipPermission(
+ ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
+ if (result != CODE_OK) {
+ throw new ServiceSpecificException(
+ PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+ "Provisioning preconditions failed with result: " + result);
+ }
}
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
@@ -16336,6 +16322,12 @@
.setPackage(getManagedProvisioningPackage(mContext))
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ } catch (Exception e) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
+ .setStrings(callerPackage)
+ .write();
+ throw e;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -16412,6 +16404,20 @@
private boolean setActiveAdminAndDeviceOwner(
@UserIdInt int userId, ComponentName adminComponent, String name) {
enableAndSetActiveAdmin(userId, userId, adminComponent);
- return setDeviceOwner(adminComponent, name, userId);
+ // TODO(b/178187130): Directly set DO and remove the check once silent provisioning is no
+ // longer used.
+ if (getDeviceOwnerComponent(/* callingUserOnly= */ true) == null) {
+ return setDeviceOwner(adminComponent, name, userId);
+ }
+ return true;
+ }
+
+ private static void logEventDuration(int eventId, long startTime, String callerPackage) {
+ final long duration = SystemClock.elapsedRealtime() - startTime;
+ DevicePolicyEventLogger
+ .createEvent(eventId)
+ .setTimePeriod(duration)
+ .setStrings(callerPackage)
+ .write();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 22866b4..fc1d831 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -73,25 +73,28 @@
pw.printf(" Prints this help text.\n\n");
pw.printf(" %s <OPERATION_ID>\n", CMD_IS_SAFE_OPERATION);
pw.printf(" Checks if the give operation is safe \n\n");
- pw.printf(" %s <OPERATION_ID> <true|false>\n", CMD_SET_SAFE_OPERATION);
+ pw.printf(" %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION);
pw.printf(" Emulates the result of the next call to check if the given operation is safe"
+ " \n\n");
}
private int runIsSafeOperation(PrintWriter pw) {
int operation = Integer.parseInt(getNextArgRequired());
- boolean safe = mService.canExecute(operation);
- pw.printf("Operation %s is %s\n", DevicePolicyManager.operationToString(operation),
- safe ? "SAFE" : "UNSAFE");
+ int reason = mService.getUnsafeOperationReason(operation);
+ boolean safe = reason == DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
+ pw.printf("Operation %s is %b. Reason: %s\n",
+ DevicePolicyManager.operationToString(operation), safe,
+ DevicePolicyManager.unsafeOperationReasonToString(reason));
return 0;
}
private int runSetSafeOperation(PrintWriter pw) {
int operation = Integer.parseInt(getNextArgRequired());
- boolean safe = getNextArg().equals("true");
- mService.setNextOperationSafety(operation, safe);
+ int reason = Integer.parseInt(getNextArgRequired());
+ mService.setNextOperationSafety(operation, reason);
pw.printf("Next call to check operation %s will return %s\n",
- DevicePolicyManager.operationToString(operation), safe ? "SAFE" : "UNSAFE");
+ DevicePolicyManager.operationToString(operation),
+ DevicePolicyManager.unsafeOperationReasonToString(reason));
return 0;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index f7a8261..883f95d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -15,9 +15,12 @@
*/
package com.android.server.devicepolicy;
+import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
import static android.app.admin.DevicePolicyManager.operationToString;
+import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.app.admin.DevicePolicySafetyChecker;
import android.util.Slog;
@@ -40,31 +43,33 @@
private final DevicePolicyManagerService mService;
private final DevicePolicySafetyChecker mRealSafetyChecker;
private final @DevicePolicyOperation int mOperation;
- private final boolean mSafe;
+ private final @UnsafeOperationReason int mReason;
OneTimeSafetyChecker(DevicePolicyManagerService service,
- @DevicePolicyOperation int operation, boolean safe) {
+ @DevicePolicyOperation int operation, @UnsafeOperationReason int reason) {
mService = Objects.requireNonNull(service);
mOperation = operation;
- mSafe = safe;
+ mReason = reason;
mRealSafetyChecker = service.getDevicePolicySafetyChecker();
Slog.i(TAG, "Saving real DevicePolicySafetyChecker as " + mRealSafetyChecker);
}
@Override
- public boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) {
+ @UnsafeOperationReason
+ public int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
String name = operationToString(operation);
- boolean safe = true;
+ int reason = UNSAFE_OPERATION_REASON_NONE;
if (operation == mOperation) {
- safe = mSafe;
+ reason = mReason;
} else {
Slog.wtf(TAG, "invalid call to isDevicePolicyOperationSafe(): asked for " + name
+ ", should be " + operationToString(mOperation));
}
- Slog.i(TAG, "isDevicePolicyOperationSafe(" + name + "): returning " + safe
+ Slog.i(TAG, "getDevicePolicyOperationSafety(" + name + "): returning "
+ + unsafeOperationReasonToString(reason)
+ " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker);
- return safe;
+ return reason;
}
@Override
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index d224428..42360d8 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -88,7 +88,6 @@
}
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
- ps->giveThreadPoolName();
// sm->addService increments the reference count, and now we're OK with returning the pointer.
return self.get();
}
@@ -118,18 +117,10 @@
binder::Status BinderIncrementalService::createStorage(
const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params,
- int32_t createMode,
- const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
- const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
- const ::android::sp<::android::os::incremental::IStorageHealthListener>& healthListener,
- const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
- int32_t* _aidl_return) {
- *_aidl_return =
- mImpl.createStorage(path, const_cast<content::pm::DataLoaderParamsParcel&&>(params),
- android::incremental::IncrementalService::CreateOptions(createMode),
- statusListener,
- const_cast<StorageHealthCheckParams&&>(healthCheckParams),
- healthListener, perUidReadTimeouts);
+ int32_t createMode, int32_t* _aidl_return) {
+ *_aidl_return = mImpl.createStorage(path, params,
+ android::incremental::IncrementalService::CreateOptions(
+ createMode));
return ok();
}
@@ -144,6 +135,21 @@
return ok();
}
+binder::Status BinderIncrementalService::startLoading(
+ int32_t storageId, const ::android::content::pm::DataLoaderParamsParcel& params,
+ const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
+ const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
+ const ::android::sp<IStorageHealthListener>& healthListener,
+ const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
+ bool* _aidl_return) {
+ *_aidl_return =
+ mImpl.startLoading(storageId, const_cast<content::pm::DataLoaderParamsParcel&&>(params),
+ statusListener,
+ const_cast<StorageHealthCheckParams&&>(healthCheckParams),
+ healthListener, perUidReadTimeouts);
+ return ok();
+}
+
binder::Status BinderIncrementalService::makeBindMount(int32_t storageId,
const std::string& sourcePath,
const std::string& targetFullPath,
@@ -253,9 +259,16 @@
return ok();
}
+binder::Status BinderIncrementalService::isFullyLoaded(int32_t storageId, int32_t* _aidl_return) {
+ *_aidl_return = mImpl.getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/true)
+ .blocksRemainingOrError();
+ return ok();
+}
+
binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId,
float* _aidl_return) {
- *_aidl_return = mImpl.getLoadingProgress(storageId).getProgress();
+ *_aidl_return =
+ mImpl.getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false).getProgress();
return ok();
}
@@ -291,11 +304,6 @@
return ok();
}
-binder::Status BinderIncrementalService::startLoading(int32_t storageId, bool* _aidl_return) {
- *_aidl_return = mImpl.startLoading(storageId);
- return ok();
-}
-
binder::Status BinderIncrementalService::configureNativeBinaries(
int32_t storageId, const std::string& apkFullPath, const std::string& libDirRelativePath,
const std::string& abi, bool extractNativeLibs, bool* _aidl_return) {
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 9a4537a..740c542 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -39,16 +39,18 @@
void onInvalidStorage(int mountId);
binder::Status openStorage(const std::string& path, int32_t* _aidl_return) final;
- binder::Status createStorage(
- const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params,
- int32_t createMode,
+ binder::Status createStorage(const ::std::string& path,
+ const ::android::content::pm::DataLoaderParamsParcel& params,
+ int32_t createMode, int32_t* _aidl_return) final;
+ binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId,
+ int32_t createMode, int32_t* _aidl_return) final;
+ binder::Status startLoading(
+ int32_t storageId, const ::android::content::pm::DataLoaderParamsParcel& params,
const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
const ::android::sp<IStorageHealthListener>& healthListener,
const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
- int32_t* _aidl_return) final;
- binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId,
- int32_t createMode, int32_t* _aidl_return) final;
+ bool* _aidl_return) final;
binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath,
const std::string& targetFullPath, int32_t bindType,
int32_t* _aidl_return) final;
@@ -71,12 +73,12 @@
binder::Status unlink(int32_t storageId, const std::string& path, int32_t* _aidl_return) final;
binder::Status isFileFullyLoaded(int32_t storageId, const std::string& path,
int32_t* _aidl_return) final;
+ binder::Status isFullyLoaded(int32_t storageId, int32_t* _aidl_return) final;
binder::Status getLoadingProgress(int32_t storageId, float* _aidl_return) final;
binder::Status getMetadataByPath(int32_t storageId, const std::string& path,
std::vector<uint8_t>* _aidl_return) final;
binder::Status getMetadataById(int32_t storageId, const std::vector<uint8_t>& id,
std::vector<uint8_t>* _aidl_return) final;
- binder::Status startLoading(int32_t storageId, bool* _aidl_return) final;
binder::Status deleteStorage(int32_t storageId) final;
binder::Status disallowReadLogs(int32_t storageId) final;
binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index c9c5489..56cb3d1 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -356,7 +356,9 @@
dprintf(fd, " storages (%d): {\n", int(mnt.storages.size()));
for (auto&& [storageId, storage] : mnt.storages) {
dprintf(fd, " [%d] -> [%s] (%d %% loaded) \n", storageId, storage.name.c_str(),
- (int)(getLoadingProgressFromPath(mnt, storage.name.c_str()).getProgress() *
+ (int)(getLoadingProgressFromPath(mnt, storage.name.c_str(),
+ /*stopOnFirstIncomplete=*/false)
+ .getProgress() *
100));
}
dprintf(fd, " }\n");
@@ -427,10 +429,8 @@
}
StorageId IncrementalService::createStorage(
- std::string_view mountPoint, content::pm::DataLoaderParamsParcel&& dataLoaderParams,
- CreateOptions options, const DataLoaderStatusListener& statusListener,
- StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener& healthListener,
- const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
+ std::string_view mountPoint, const content::pm::DataLoaderParamsParcel& dataLoaderParams,
+ CreateOptions options) {
LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
if (!path::isAbsolute(mountPoint)) {
LOG(ERROR) << "path is not absolute: " << mountPoint;
@@ -538,13 +538,10 @@
metadata::Mount m;
m.mutable_storage()->set_id(ifs->mountId);
m.mutable_loader()->set_type((int)dataLoaderParams.type);
- m.mutable_loader()->set_allocated_package_name(&dataLoaderParams.packageName);
- m.mutable_loader()->set_allocated_class_name(&dataLoaderParams.className);
- m.mutable_loader()->set_allocated_arguments(&dataLoaderParams.arguments);
+ m.mutable_loader()->set_package_name(dataLoaderParams.packageName);
+ m.mutable_loader()->set_class_name(dataLoaderParams.className);
+ m.mutable_loader()->set_arguments(dataLoaderParams.arguments);
const auto metadata = m.SerializeAsString();
- m.mutable_loader()->release_arguments();
- m.mutable_loader()->release_class_name();
- m.mutable_loader()->release_package_name();
if (auto err =
mIncFs->makeFile(ifs->control,
path::join(ifs->root, constants().mount,
@@ -568,26 +565,9 @@
// Done here as well, all data structures are in good state.
secondCleanupOnFailure.release();
- // DataLoader.
- auto dataLoaderStub = prepareDataLoader(*ifs, std::move(dataLoaderParams), &statusListener,
- std::move(healthCheckParams), &healthListener);
- CHECK(dataLoaderStub);
-
mountIt->second = std::move(ifs);
l.unlock();
- // Per Uid timeouts.
- if (!perUidReadTimeouts.empty()) {
- setUidReadTimeouts(mountId, perUidReadTimeouts);
- }
-
- if (mSystemReady.load(std::memory_order_relaxed) && !dataLoaderStub->requestCreate()) {
- // failed to create data loader
- LOG(ERROR) << "initializeDataLoader() failed";
- deleteStorage(dataLoaderStub->id());
- return kInvalidStorageId;
- }
-
LOG(INFO) << "created storage " << mountId;
return mountId;
}
@@ -634,6 +614,37 @@
return storageId;
}
+bool IncrementalService::startLoading(StorageId storage,
+ content::pm::DataLoaderParamsParcel&& dataLoaderParams,
+ const DataLoaderStatusListener& statusListener,
+ StorageHealthCheckParams&& healthCheckParams,
+ const StorageHealthListener& healthListener,
+ const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
+ // Per Uid timeouts.
+ if (!perUidReadTimeouts.empty()) {
+ setUidReadTimeouts(storage, perUidReadTimeouts);
+ }
+
+ // Re-initialize DataLoader.
+ std::unique_lock l(mLock);
+ const auto ifs = getIfsLocked(storage);
+ if (!ifs) {
+ return false;
+ }
+ if (ifs->dataLoaderStub) {
+ ifs->dataLoaderStub->cleanupResources();
+ ifs->dataLoaderStub = {};
+ }
+ l.unlock();
+
+ // DataLoader.
+ auto dataLoaderStub = prepareDataLoader(*ifs, std::move(dataLoaderParams), &statusListener,
+ std::move(healthCheckParams), &healthListener);
+ CHECK(dataLoaderStub);
+
+ return dataLoaderStub->requestStart();
+}
+
IncrementalService::BindPathMap::const_iterator IncrementalService::findStorageLocked(
std::string_view path) const {
return findParentPath(mBindsByPath, path);
@@ -960,7 +971,12 @@
LOG(ERROR) << "Invalid paths in link(): " << normOldPath << " | " << normNewPath;
return -EINVAL;
}
- return mIncFs->link(ifsSrc->control, normOldPath, normNewPath);
+ if (auto err = mIncFs->link(ifsSrc->control, normOldPath, normNewPath); err < 0) {
+ PLOG(ERROR) << "Failed to link " << oldPath << "[" << normOldPath << "]"
+ << " to " << newPath << "[" << normNewPath << "]";
+ return err;
+ }
+ return 0;
}
int IncrementalService::unlink(StorageId storage, std::string_view path) {
@@ -1065,23 +1081,6 @@
return mIncFs->getMetadata(ifs->control, node);
}
-bool IncrementalService::startLoading(StorageId storage) const {
- DataLoaderStubPtr dataLoaderStub;
- {
- std::unique_lock l(mLock);
- const auto& ifs = getIfsLocked(storage);
- if (!ifs) {
- return false;
- }
- dataLoaderStub = ifs->dataLoaderStub;
- if (!dataLoaderStub) {
- return false;
- }
- }
- dataLoaderStub->requestStart();
- return true;
-}
-
void IncrementalService::setUidReadTimeouts(
StorageId storage, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
using microseconds = std::chrono::microseconds;
@@ -1092,11 +1091,15 @@
maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs));
}
if (maxPendingTimeUs < Constants::minPerUidTimeout) {
+ LOG(ERROR) << "Skip setting read timeouts (maxPendingTime < Constants::minPerUidTimeout): "
+ << duration_cast<milliseconds>(maxPendingTimeUs).count() << "ms < "
+ << Constants::minPerUidTimeout.count() << "ms";
return;
}
const auto ifs = getIfs(storage);
if (!ifs) {
+ LOG(ERROR) << "Setting read timeouts failed: invalid storage id: " << storage;
return;
}
@@ -1126,7 +1129,7 @@
}
// Still loading?
- const auto progress = getLoadingProgress(storage);
+ const auto progress = getLoadingProgress(storage, /*stopOnFirstIncomplete=*/true);
if (progress.isError()) {
// Something is wrong, abort.
return clearUidReadTimeouts(storage);
@@ -1840,7 +1843,7 @@
}
IncrementalService::LoadingProgress IncrementalService::getLoadingProgress(
- StorageId storage) const {
+ StorageId storage, bool stopOnFirstIncomplete) const {
std::unique_lock l(mLock);
const auto ifs = getIfsLocked(storage);
if (!ifs) {
@@ -1853,11 +1856,11 @@
return {-EINVAL, -EINVAL};
}
l.unlock();
- return getLoadingProgressFromPath(*ifs, storageInfo->second.name);
+ return getLoadingProgressFromPath(*ifs, storageInfo->second.name, stopOnFirstIncomplete);
}
IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPath(
- const IncFsMount& ifs, std::string_view storagePath) const {
+ 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) {
@@ -1870,6 +1873,9 @@
}
totalBlocks += totalBlocksCount;
filledBlocks += filledBlocksCount;
+ if (stopOnFirstIncomplete && filledBlocks < totalBlocks) {
+ break;
+ }
}
return {filledBlocks, totalBlocks};
@@ -1877,7 +1883,7 @@
bool IncrementalService::updateLoadingProgress(
StorageId storage, const StorageLoadingProgressListener& progressListener) {
- const auto progress = getLoadingProgress(storage);
+ const auto progress = getLoadingProgress(storage, /*stopOnFirstIncomplete=*/false);
if (progress.isError()) {
// Failed to get progress from incfs, abort.
return false;
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 3066121..5d53bac 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -113,6 +113,10 @@
bool started() const { return totalBlocks > 0; }
bool fullyLoaded() const { return !isError() && (totalBlocks == filledBlocks); }
+ int blocksRemainingOrError() const {
+ return totalBlocks <= 0 ? totalBlocks : totalBlocks - filledBlocks;
+ }
+
float getProgress() const {
return totalBlocks < 0
? totalBlocks
@@ -130,15 +134,18 @@
void onSystemReady();
StorageId createStorage(std::string_view mountPoint,
- content::pm::DataLoaderParamsParcel&& dataLoaderParams,
- CreateOptions options, const DataLoaderStatusListener& statusListener,
- StorageHealthCheckParams&& healthCheckParams,
- const StorageHealthListener& healthListener,
- const std::vector<PerUidReadTimeouts>& perUidReadTimeouts);
+ const content::pm::DataLoaderParamsParcel& dataLoaderParams,
+ CreateOptions options);
StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
CreateOptions options = CreateOptions::Default);
StorageId openStorage(std::string_view path);
+ bool startLoading(StorageId storage, content::pm::DataLoaderParamsParcel&& dataLoaderParams,
+ const DataLoaderStatusListener& statusListener,
+ StorageHealthCheckParams&& healthCheckParams,
+ const StorageHealthListener& healthListener,
+ const std::vector<PerUidReadTimeouts>& perUidReadTimeouts);
+
int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind);
int unbind(StorageId storage, std::string_view target);
void deleteStorage(StorageId storage);
@@ -156,7 +163,9 @@
int unlink(StorageId storage, std::string_view path);
int isFileFullyLoaded(StorageId storage, std::string_view filePath) const;
- LoadingProgress getLoadingProgress(StorageId storage) const;
+
+ LoadingProgress getLoadingProgress(StorageId storage, bool stopOnFirstIncomplete) const;
+
bool registerLoadingProgressListener(StorageId storage,
const StorageLoadingProgressListener& progressListener);
bool unregisterLoadingProgressListener(StorageId storage);
@@ -167,8 +176,6 @@
RawMetadata getMetadata(StorageId storage, std::string_view path) const;
RawMetadata getMetadata(StorageId storage, FileId node) const;
- bool startLoading(StorageId storage) const;
-
bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
std::string_view libDirRelativePath, std::string_view abi,
bool extractNativeLibs);
@@ -388,7 +395,8 @@
binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
- LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
+ LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path,
+ bool stopOnFirstIncomplete) const;
int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
std::string_view debugFilePath, std::span<const uint8_t> data) const;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index dfa6083..25d3f77 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -220,6 +220,7 @@
timeout.minPendingTimeUs = perUidTimeout.minPendingTimeUs;
timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs;
}
+
return incfs::setUidReadTimeouts(control, timeouts);
}
};
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index f0deba7..8713f9d 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -678,9 +678,9 @@
mVold->mountIncFsFails();
EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
@@ -689,9 +689,9 @@
EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
@@ -702,9 +702,9 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
@@ -716,9 +716,9 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
@@ -734,24 +734,24 @@
EXPECT_CALL(*mDataLoader, destroy(_)).Times(0);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
- ASSERT_LT(storageId, 0);
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
}
TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
- EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
- EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(1);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
mIncrementalService->deleteStorage(storageId);
}
@@ -759,14 +759,15 @@
EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
- EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(2);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
// Simulated crash/other connection breakage.
mDataLoaderManager->setDataLoaderStatusDestroyed();
}
@@ -780,12 +781,13 @@
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
mDataLoaderManager->setDataLoaderStatusCreated();
- ASSERT_TRUE(mIncrementalService->startLoading(storageId));
mDataLoaderManager->setDataLoaderStatusStarted();
}
@@ -793,16 +795,17 @@
mDataLoader->initializeCreateOkNoStatus();
EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
- EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- ASSERT_TRUE(mIncrementalService->startLoading(storageId));
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
mDataLoaderManager->setDataLoaderStatusCreated();
}
@@ -815,10 +818,12 @@
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
mDataLoaderManager->setDataLoaderStatusUnavailable();
}
@@ -836,10 +841,12 @@
EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(1);
EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(1);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
mDataLoaderManager->setDataLoaderStatusUnavailable();
ASSERT_NE(nullptr, mLooper->mCallback);
ASSERT_NE(nullptr, mLooper->mCallbackData);
@@ -890,10 +897,12 @@
kFirstTimestampUs - std::chrono::duration_cast<MCS>(unhealthyTimeout).count();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, std::move(params), listener, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {},
+ std::move(params), listener, {});
// Healthy state, registered for pending reads.
ASSERT_NE(nullptr, mLooper->mCallback);
@@ -985,10 +994,12 @@
// Not expecting callback removal.
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
}
@@ -1006,10 +1017,12 @@
// Not expecting callback removal.
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
// Now disable.
mIncrementalService->disallowReadLogs(storageId);
@@ -1032,10 +1045,12 @@
// After callback is called, disable read logs and remove callback.
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(1);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get());
mAppOpsManager->mStoredCallback->opChanged(0, {});
@@ -1051,10 +1066,12 @@
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
ASSERT_LT(mDataLoader->setStorageParams(true), 0);
}
@@ -1068,10 +1085,12 @@
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
ASSERT_LT(mDataLoader->setStorageParams(true), 0);
}
@@ -1087,18 +1106,20 @@
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
ASSERT_LT(mDataLoader->setStorageParams(true), 0);
}
TEST_F(IncrementalServiceTest, testMakeDirectory) {
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
std::string dir_path("test");
// Expecting incfs to call makeDir on a path like:
@@ -1115,9 +1136,9 @@
TEST_F(IncrementalServiceTest, testMakeDirectories) {
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
auto first = "first"sv;
auto second = "second"sv;
auto third = "third"sv;
@@ -1138,9 +1159,9 @@
mFs->hasNoFile();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
}
@@ -1149,9 +1170,9 @@
mFs->hasFiles();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
}
@@ -1161,9 +1182,9 @@
mFs->hasFiles();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
}
@@ -1173,9 +1194,9 @@
mFs->hasFiles();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
}
@@ -1185,10 +1206,12 @@
mFs->hasNoFile();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
- ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress());
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_EQ(1,
+ mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false)
+ .getProgress());
}
TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) {
@@ -1196,11 +1219,13 @@
mFs->hasFiles();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
- ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId).getProgress());
+ ASSERT_EQ(-1,
+ mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false)
+ .getProgress());
}
TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) {
@@ -1208,11 +1233,13 @@
mFs->hasFiles();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
- ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress());
+ ASSERT_EQ(1,
+ mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false)
+ .getProgress());
}
TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) {
@@ -1220,11 +1247,13 @@
mFs->hasFiles();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
- ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId).getProgress());
+ ASSERT_EQ(0.5,
+ mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false)
+ .getProgress());
}
TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) {
@@ -1232,9 +1261,9 @@
mFs->hasFiles();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
sp<NiceMock<MockStorageLoadingProgressListener>> listener{
new NiceMock<MockStorageLoadingProgressListener>};
NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
@@ -1257,9 +1286,9 @@
mFs->hasFiles();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, {}, {}, {});
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
sp<NiceMock<MockStorageLoadingProgressListener>> listener{
new NiceMock<MockStorageLoadingProgressListener>};
NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
@@ -1275,10 +1304,12 @@
TemporaryDir tempDir;
int storageId =
- mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew, {},
- StorageHealthCheckParams{}, listener, {});
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, listener,
+ {});
+
StorageHealthCheckParams newParams;
newParams.blockedTimeoutMs = 10000;
newParams.unhealthyTimeoutMs = 20000;
@@ -1378,19 +1409,19 @@
EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
- EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(1);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0);
EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
- mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew, {}, {},
- {},
- createPerUidTimeouts(
- {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}}));
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {},
+ createPerUidTimeouts(
+ {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}}));
}
TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) {
@@ -1410,13 +1441,12 @@
TemporaryDir tempDir;
int storageId =
- mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew, {}, {},
- {},
- createPerUidTimeouts({{0, 1, 2, 3},
- {1, 2, 3, 4},
- {2, 3, 4, 100000000}}));
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {},
+ createPerUidTimeouts(
+ {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 100000000}}));
{
// Timed callback present -> 0 progress.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d28c3cc..636be4a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -97,6 +97,7 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
+import com.android.internal.policy.AttributeCache;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.FrameworkStatsLog;
@@ -341,6 +342,8 @@
"com.android.server.contentsuggestions.ContentSuggestionsManagerService";
private static final String SEARCH_UI_MANAGER_SERVICE_CLASS =
"com.android.server.searchui.SearchUiManagerService";
+ private static final String SMARTSPACE_MANAGER_SERVICE_CLASS =
+ "com.android.server.smartspace.SmartspaceManagerService";
private static final String DEVICE_IDLE_CONTROLLER_CLASS =
"com.android.server.DeviceIdleController";
private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
@@ -359,7 +362,12 @@
"com.android.server.ConnectivityServiceInitializer";
private static final String IP_CONNECTIVITY_METRICS_CLASS =
"com.android.server.connectivity.IpConnectivityMetrics";
+ private static final String MEDIA_COMMUNICATION_SERVICE_CLASS =
+ "com.android.server.media.MediaCommunicationService";
+
private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
+ private static final String GAME_MANAGER_SERVICE_CLASS =
+ "com.android.server.app.GameManagerService$Lifecycle";
private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
@@ -1336,6 +1344,13 @@
mSystemServiceManager.startService(DropBoxManagerService.class);
t.traceEnd();
+ // Grants default permissions and defines roles
+ t.traceBegin("StartRoleManagerService");
+ LocalManagerRegistry.addManager(RoleServicePlatformHelper.class,
+ new RoleServicePlatformHelperImpl(mSystemContext));
+ mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
+ t.traceEnd();
+
t.traceBegin("StartVibratorManagerService");
mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
t.traceEnd();
@@ -1670,6 +1685,12 @@
mSystemServiceManager.startService(SEARCH_UI_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ // Smartspace manager service
+ // TODO: add deviceHasConfigString(context, R.string.config_defaultSmartspaceService)
+ t.traceBegin("StartSmartspaceService");
+ mSystemServiceManager.startService(SMARTSPACE_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+
t.traceBegin("InitConnectivityModuleConnector");
try {
ConnectivityModuleConnector.getInstance().init(context);
@@ -2046,13 +2067,6 @@
t.traceEnd();
}
- // Grants default permissions and defines roles
- t.traceBegin("StartRoleManagerService");
- LocalManagerRegistry.addManager(RoleServicePlatformHelper.class,
- new RoleServicePlatformHelperImpl(mSystemContext));
- mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
- t.traceEnd();
-
// We need to always start this service, regardless of whether the
// FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
// of initializing various settings. It will internally modify its behavior
@@ -2514,6 +2528,10 @@
}
t.traceEnd();
+ t.traceBegin("GameManagerService");
+ mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+
t.traceBegin("StartBootPhaseDeviceSpecificServicesReady");
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
t.traceEnd();
@@ -2522,6 +2540,10 @@
mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ t.traceBegin("StartMediaCommunicationService");
+ mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS);
+ t.traceEnd();
+
ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
START_BLOB_STORE_SERVICE);
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 9a9a171..444f9c6 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -264,7 +264,8 @@
return new ConversationChannel(shortcutInfo, uid, parentChannel,
parentChannelGroup,
conversationInfo.getLastEventTimestamp(),
- hasActiveNotifications(packageName, userId, shortcutId));
+ hasActiveNotifications(packageName, userId, shortcutId), false,
+ getStatuses(conversationInfo));
}
/** Returns the cached non-customized recent conversations. */
@@ -404,6 +405,10 @@
String conversationId) {
ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
ConversationInfo conversationInfo = getConversationInfoOrThrow(cs, conversationId);
+ return getStatuses(conversationInfo);
+ }
+
+ private @NonNull List<ConversationStatus> getStatuses(ConversationInfo conversationInfo) {
Collection<ConversationStatus> statuses = conversationInfo.getStatuses();
if (statuses != null) {
final ArrayList<ConversationStatus> list = new ArrayList<>(statuses.size());
diff --git a/services/smartspace/Android.bp b/services/smartspace/Android.bp
new file mode 100644
index 0000000..fcf780d
--- /dev/null
+++ b/services/smartspace/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+ name: "services.smartspace-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.smartspace",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.smartspace-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/smartspace/OWNERS b/services/smartspace/OWNERS
new file mode 100644
index 0000000..19ef9d7
--- /dev/null
+++ b/services/smartspace/OWNERS
@@ -0,0 +1,2 @@
+srazdan@google.com
+alexmang@google.com
\ No newline at end of file
diff --git a/services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java b/services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java
new file mode 100644
index 0000000..3b5a5a5
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java
@@ -0,0 +1,112 @@
+/*
+ * 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.smartspace;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.service.smartspace.ISmartspaceService;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+
+/**
+ * Proxy to the {@link android.service.smartspace.SmartspaceService} implementation in another
+ * process.
+ */
+public class RemoteSmartspaceService extends
+ AbstractMultiplePendingRequestsRemoteService<RemoteSmartspaceService,
+ ISmartspaceService> {
+
+ private static final String TAG = "RemoteSmartspaceService";
+
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+ private final RemoteSmartspaceServiceCallbacks mCallback;
+
+ public RemoteSmartspaceService(Context context, String serviceInterface,
+ ComponentName componentName, int userId,
+ RemoteSmartspaceServiceCallbacks callback, boolean bindInstantServiceAllowed,
+ boolean verbose) {
+ super(context, serviceInterface, componentName, userId, callback,
+ context.getMainThreadHandler(),
+ bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
+ verbose, /* initialCapacity= */ 1);
+ mCallback = callback;
+ }
+
+ @Override
+ protected ISmartspaceService getServiceInterface(IBinder service) {
+ return ISmartspaceService.Stub.asInterface(service);
+ }
+
+ @Override
+ protected long getTimeoutIdleBindMillis() {
+ return PERMANENT_BOUND_TIMEOUT_MS;
+ }
+
+ @Override
+ protected long getRemoteRequestMillis() {
+ return TIMEOUT_REMOTE_REQUEST_MILLIS;
+ }
+
+ /**
+ * Schedules a request to bind to the remote service.
+ */
+ public void reconnect() {
+ super.scheduleBind();
+ }
+
+ /**
+ * Schedule async request on remote service.
+ */
+ public void scheduleOnResolvedService(@NonNull AsyncRequest<ISmartspaceService> request) {
+ scheduleAsyncRequest(request);
+ }
+
+ /**
+ * Execute async request on remote service immediately instead of sending it to Handler queue.
+ */
+ public void executeOnResolvedService(@NonNull AsyncRequest<ISmartspaceService> request) {
+ executeAsyncRequest(request);
+ }
+
+ /**
+ * Failure callback
+ */
+ public interface RemoteSmartspaceServiceCallbacks
+ extends VultureCallback<RemoteSmartspaceService> {
+
+ /**
+ * Notifies a the failure or timeout of a remote call.
+ */
+ void onFailureOrTimeout(boolean timedOut);
+
+ /**
+ * Notifies change in connected state of the remote service.
+ */
+ void onConnectedStateChanged(boolean connected);
+ }
+
+ @Override // from AbstractRemoteService
+ protected void handleOnConnectedStateChanged(boolean connected) {
+ if (mCallback != null) {
+ mCallback.onConnectedStateChanged(connected);
+ }
+ }
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
new file mode 100644
index 0000000..169b85e
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
@@ -0,0 +1,184 @@
+/*
+ * 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.smartspace;
+
+import static android.Manifest.permission.MANAGE_SMARTSPACE;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.content.Context.SMARTSPACE_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.app.smartspace.ISmartspaceCallback;
+import android.app.smartspace.ISmartspaceManager;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.FileDescriptor;
+import java.util.function.Consumer;
+
+/**
+ * A service used to return smartspace targets given a query.
+ */
+public class SmartspaceManagerService extends
+ AbstractMasterSystemService<SmartspaceManagerService, SmartspacePerUserService> {
+
+ private static final String TAG = SmartspaceManagerService.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+ private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+
+ public SmartspaceManagerService(Context context) {
+ super(context, new FrameworkResourcesServiceNameResolver(context,
+ com.android.internal.R.string.config_defaultSmartspaceService), null,
+ PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
+ mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ }
+
+ @Override
+ protected SmartspacePerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
+ return new SmartspacePerUserService(this, mLock, resolvedUserId);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(SMARTSPACE_SERVICE, new SmartspaceManagerStub());
+ }
+
+ @Override
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_SMARTSPACE, TAG);
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
+ final SmartspacePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.onPackageUpdatedLocked();
+ }
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
+ final SmartspacePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.onPackageRestartedLocked();
+ }
+ }
+
+ @Override
+ protected int getMaximumTemporaryServiceDurationMs() {
+ return MAX_TEMP_SERVICE_DURATION_MS;
+ }
+
+ private class SmartspaceManagerStub extends ISmartspaceManager.Stub {
+
+ @Override
+ public void createSmartspaceSession(@NonNull SmartspaceConfig smartspaceConfig,
+ @NonNull SmartspaceSessionId sessionId, @NonNull IBinder token) {
+ runForUserLocked("createSmartspaceSession", sessionId, (service) ->
+ service.onCreateSmartspaceSessionLocked(smartspaceConfig, sessionId, token));
+ }
+
+ @Override
+ public void notifySmartspaceEvent(SmartspaceSessionId sessionId,
+ SmartspaceTargetEvent event) {
+ runForUserLocked("notifySmartspaceEvent", sessionId,
+ (service) -> service.notifySmartspaceEventLocked(sessionId, event));
+ }
+
+ @Override
+ public void requestSmartspaceUpdate(SmartspaceSessionId sessionId) {
+ runForUserLocked("requestSmartspaceUpdate", sessionId,
+ (service) -> service.requestSmartspaceUpdateLocked(sessionId));
+ }
+
+ @Override
+ public void registerSmartspaceUpdates(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ runForUserLocked("registerSmartspaceUpdates", sessionId,
+ (service) -> service.registerSmartspaceUpdatesLocked(sessionId, callback));
+ }
+
+ @Override
+ public void unregisterSmartspaceUpdates(SmartspaceSessionId sessionId,
+ ISmartspaceCallback callback) {
+ runForUserLocked("unregisterSmartspaceUpdates", sessionId,
+ (service) -> service.unregisterSmartspaceUpdatesLocked(sessionId, callback));
+ }
+
+ @Override
+ public void destroySmartspaceSession(@NonNull SmartspaceSessionId sessionId) {
+ runForUserLocked("destroySmartspaceSession", sessionId,
+ (service) -> service.onDestroyLocked(sessionId));
+ }
+
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) {
+ new SmartspaceManagerServiceShellCommand(SmartspaceManagerService.this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ private void runForUserLocked(@NonNull final String func,
+ @NonNull final SmartspaceSessionId sessionId,
+ @NonNull final Consumer<SmartspacePerUserService> c) {
+ ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
+ final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ sessionId.getUserId(), false, ALLOW_NON_FULL, null, null);
+
+ if (DEBUG) {
+ Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ }
+ if (!(mServiceNameResolver.isTemporary(userId)
+ || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
+
+ String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid();
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ final SmartspacePerUserService service = getServiceForUserLocked(userId);
+ c.accept(service);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java
new file mode 100644
index 0000000..4143418e
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java
@@ -0,0 +1,84 @@
+/*
+ * 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.smartspace;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * The shell command implementation for the SmartspaceManagerService.
+ */
+public class SmartspaceManagerServiceShellCommand extends ShellCommand {
+
+ private static final String TAG =
+ SmartspaceManagerServiceShellCommand.class.getSimpleName();
+
+ private final SmartspaceManagerService mService;
+
+ public SmartspaceManagerServiceShellCommand(@NonNull SmartspaceManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "set": {
+ final String what = getNextArgRequired();
+ switch (what) {
+ case "temporary-service": {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ pw.println("SmartspaceService temporarily reset. ");
+ return 0;
+ }
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryService(userId, serviceName, duration);
+ pw.println("SmartspaceService temporarily set to " + serviceName
+ + " for " + duration + "ms");
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter()) {
+ pw.println("SmartspaceManagerService commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println("");
+ }
+ }
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java
new file mode 100644
index 0000000..db43468
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java
@@ -0,0 +1,411 @@
+/*
+ * 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.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.app.smartspace.ISmartspaceCallback;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.service.smartspace.ISmartspaceService;
+import android.service.smartspace.SmartspaceService;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+/**
+ * Per-user instance of {@link SmartspaceManagerService}.
+ */
+public class SmartspacePerUserService extends
+ AbstractPerUserSystemService<SmartspacePerUserService, SmartspaceManagerService>
+ implements RemoteSmartspaceService.RemoteSmartspaceServiceCallbacks {
+
+ private static final String TAG = SmartspacePerUserService.class.getSimpleName();
+ @GuardedBy("mLock")
+ private final ArrayMap<SmartspaceSessionId, SmartspaceSessionInfo> mSessionInfos =
+ new ArrayMap<>();
+ @Nullable
+ @GuardedBy("mLock")
+ private RemoteSmartspaceService mRemoteService;
+ /**
+ * When {@code true}, remote service died but service state is kept so it's restored after
+ * the system re-binds to it.
+ */
+ @GuardedBy("mLock")
+ private boolean mZombie;
+
+ protected SmartspacePerUserService(SmartspaceManagerService master,
+ Object lock, int userId) {
+ super(master, lock, userId);
+ }
+
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws NameNotFoundException {
+
+ ServiceInfo si;
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ PackageManager.GET_META_DATA, mUserId);
+ } catch (RemoteException e) {
+ throw new NameNotFoundException("Could not get service for " + serviceComponent);
+ }
+ // TODO(b/177858728): must check that either the service is from a system component,
+ // or it matches a service set by shell cmd (so it can be used on CTS tests and when
+ // OEMs are implementing the real service and also verify the proper permissions
+ return si;
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected boolean updateLocked(boolean disabled) {
+ final boolean enabledChanged = super.updateLocked(disabled);
+ if (enabledChanged) {
+ if (!isEnabledLocked()) {
+ // Clear the remote service for the next call
+ updateRemoteServiceLocked();
+ }
+ }
+ return enabledChanged;
+ }
+
+ /**
+ * Notifies the service of a new smartspace session.
+ */
+ @GuardedBy("mLock")
+ public void onCreateSmartspaceSessionLocked(@NonNull SmartspaceConfig smartspaceConfig,
+ @NonNull SmartspaceSessionId sessionId, @NonNull IBinder token) {
+ final boolean serviceExists = resolveService(sessionId,
+ s -> s.onCreateSmartspaceSession(smartspaceConfig, sessionId));
+
+ if (serviceExists && !mSessionInfos.containsKey(sessionId)) {
+ final SmartspaceSessionInfo sessionInfo = new SmartspaceSessionInfo(
+ sessionId, smartspaceConfig, token, () -> {
+ synchronized (mLock) {
+ onDestroyLocked(sessionId);
+ }
+ });
+ if (sessionInfo.linkToDeath()) {
+ mSessionInfos.put(sessionId, sessionInfo);
+ } else {
+ // destroy the session if calling process is already dead
+ onDestroyLocked(sessionId);
+ }
+ }
+ }
+
+ /**
+ * Records an smartspace event to the service.
+ */
+ @GuardedBy("mLock")
+ public void notifySmartspaceEventLocked(@NonNull SmartspaceSessionId sessionId,
+ @NonNull SmartspaceTargetEvent event) {
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (sessionInfo == null) return;
+ resolveService(sessionId, s -> s.notifySmartspaceEvent(sessionId, event));
+ }
+
+ /**
+ * Requests the service to return smartspace results of an input query.
+ */
+ @GuardedBy("mLock")
+ public void requestSmartspaceUpdateLocked(@NonNull SmartspaceSessionId sessionId) {
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (sessionInfo == null) return;
+ resolveService(sessionId,
+ s -> s.requestSmartspaceUpdate(sessionId));
+ }
+
+ /**
+ * Registers a callback for continuous updates of predicted apps or shortcuts.
+ */
+ @GuardedBy("mLock")
+ public void registerSmartspaceUpdatesLocked(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (sessionInfo == null) return;
+ final boolean serviceExists = resolveService(sessionId,
+ s -> s.registerSmartspaceUpdates(sessionId, callback));
+ if (serviceExists) {
+ sessionInfo.addCallbackLocked(callback);
+ }
+ }
+
+ /**
+ * Unregisters a callback for continuous updates of predicted apps or shortcuts.
+ */
+ @GuardedBy("mLock")
+ public void unregisterSmartspaceUpdatesLocked(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (sessionInfo == null) return;
+ final boolean serviceExists = resolveService(sessionId,
+ s -> s.unregisterSmartspaceUpdates(sessionId, callback));
+ if (serviceExists) {
+ sessionInfo.removeCallbackLocked(callback);
+ }
+ }
+
+ /**
+ * Notifies the service of the end of an existing smartspace session.
+ */
+ @GuardedBy("mLock")
+ public void onDestroyLocked(@NonNull SmartspaceSessionId sessionId) {
+ if (isDebug()) {
+ Slog.d(TAG, "onDestroyLocked(): sessionId=" + sessionId);
+ }
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.remove(sessionId);
+ if (sessionInfo == null) return;
+ resolveService(sessionId, s -> s.onDestroySmartspaceSession(sessionId));
+ sessionInfo.destroy();
+ }
+
+ @Override
+ public void onFailureOrTimeout(boolean timedOut) {
+ if (isDebug()) {
+ Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
+ }
+ // Do nothing, we are just proxying to the smartspace ui service
+ }
+
+ @Override
+ public void onConnectedStateChanged(boolean connected) {
+ if (isDebug()) {
+ Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected);
+ }
+ if (connected) {
+ synchronized (mLock) {
+ if (mZombie) {
+ // Validation check - shouldn't happen
+ if (mRemoteService == null) {
+ Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
+ return;
+ }
+ mZombie = false;
+ resurrectSessionsLocked();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDied(RemoteSmartspaceService service) {
+ if (isDebug()) {
+ Slog.w(TAG, "onServiceDied(): service=" + service);
+ }
+ synchronized (mLock) {
+ mZombie = true;
+ }
+ updateRemoteServiceLocked();
+ }
+
+ @GuardedBy("mLock")
+ private void updateRemoteServiceLocked() {
+ if (mRemoteService != null) {
+ mRemoteService.destroy();
+ mRemoteService = null;
+ }
+ }
+
+ void onPackageUpdatedLocked() {
+ if (isDebug()) {
+ Slog.v(TAG, "onPackageUpdatedLocked()");
+ }
+ destroyAndRebindRemoteService();
+ }
+
+ void onPackageRestartedLocked() {
+ if (isDebug()) {
+ Slog.v(TAG, "onPackageRestartedLocked()");
+ }
+ destroyAndRebindRemoteService();
+ }
+
+ private void destroyAndRebindRemoteService() {
+ if (mRemoteService == null) {
+ return;
+ }
+
+ if (isDebug()) {
+ Slog.d(TAG, "Destroying the old remote service.");
+ }
+ mRemoteService.destroy();
+ mRemoteService = null;
+
+ synchronized (mLock) {
+ mZombie = true;
+ }
+ mRemoteService = getRemoteServiceLocked();
+ if (mRemoteService != null) {
+ if (isDebug()) {
+ Slog.d(TAG, "Rebinding to the new remote service.");
+ }
+ mRemoteService.reconnect();
+ }
+ }
+
+ /**
+ * Called after the remote service connected, it's used to restore state from a 'zombie'
+ * service (i.e., after it died).
+ */
+ private void resurrectSessionsLocked() {
+ final int numSessions = mSessionInfos.size();
+ if (isDebug()) {
+ Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
+ + numSessions + " sessions.");
+ }
+
+ for (SmartspaceSessionInfo sessionInfo : mSessionInfos.values()) {
+ sessionInfo.resurrectSessionLocked(this, sessionInfo.mToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ protected boolean resolveService(
+ @NonNull final SmartspaceSessionId sessionId,
+ @NonNull final AbstractRemoteService.AsyncRequest<ISmartspaceService> cb) {
+
+ final RemoteSmartspaceService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.executeOnResolvedService(cb);
+ }
+ return service != null;
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSmartspaceService getRemoteServiceLocked() {
+ if (mRemoteService == null) {
+ final String serviceName = getComponentNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "getRemoteServiceLocked(): not set");
+ }
+ return null;
+ }
+ ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+ mRemoteService = new RemoteSmartspaceService(getContext(),
+ SmartspaceService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
+ mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+ }
+
+ return mRemoteService;
+ }
+
+ private static final class SmartspaceSessionInfo {
+ private static final boolean DEBUG = false; // Do not submit with true
+ @NonNull
+ final IBinder mToken;
+ @NonNull
+ final IBinder.DeathRecipient mDeathRecipient;
+ @NonNull
+ private final SmartspaceSessionId mSessionId;
+ @NonNull
+ private final SmartspaceConfig mSmartspaceConfig;
+ private final RemoteCallbackList<ISmartspaceCallback> mCallbacks =
+ new RemoteCallbackList<ISmartspaceCallback>() {
+ @Override
+ public void onCallbackDied(ISmartspaceCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "Binder died for session Id=" + mSessionId
+ + " and callback=" + callback.asBinder());
+ }
+ if (mCallbacks.getRegisteredCallbackCount() == 0) {
+ destroy();
+ }
+ }
+ };
+
+ SmartspaceSessionInfo(
+ @NonNull final SmartspaceSessionId id,
+ @NonNull final SmartspaceConfig context,
+ @NonNull final IBinder token,
+ @NonNull final IBinder.DeathRecipient deathRecipient) {
+ if (DEBUG) {
+ Slog.d(TAG, "Creating SmartspaceSessionInfo for session Id=" + id);
+ }
+ mSessionId = id;
+ mSmartspaceConfig = context;
+ mToken = token;
+ mDeathRecipient = deathRecipient;
+ }
+
+ void addCallbackLocked(ISmartspaceCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "Storing callback for session Id=" + mSessionId
+ + " and callback=" + callback.asBinder());
+ }
+ mCallbacks.register(callback);
+ }
+
+ void removeCallbackLocked(ISmartspaceCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing callback for session Id=" + mSessionId
+ + " and callback=" + callback.asBinder());
+ }
+ mCallbacks.unregister(callback);
+ }
+
+ boolean linkToDeath() {
+ try {
+ mToken.linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "Caller is dead before session can be started, sessionId: "
+ + mSessionId);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ void destroy() {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing all callbacks for session Id=" + mSessionId
+ + " and " + mCallbacks.getRegisteredCallbackCount() + " callbacks.");
+ }
+ if (mToken != null) {
+ mToken.unlinkToDeath(mDeathRecipient, 0);
+ }
+ mCallbacks.kill();
+ }
+
+ void resurrectSessionLocked(SmartspacePerUserService service, IBinder token) {
+ int callbackCount = mCallbacks.getRegisteredCallbackCount();
+ if (DEBUG) {
+ Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
+ + ") for session Id=" + mSessionId + " and "
+ + callbackCount + " callbacks.");
+ }
+ service.onCreateSmartspaceSessionLocked(mSmartspaceConfig, mSessionId, token);
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
index 17f326f..a18632b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -118,8 +118,12 @@
@After
public void tearDown() {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
- mMockitoSession.finishMocking();
- mHandlerThread.quit();
+ if (mMockitoSession != null) {
+ mMockitoSession.finishMocking();
+ }
+ if (mHandlerThread != null) {
+ mHandlerThread.quit();
+ }
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index e9a50b3..9441ecf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -164,24 +164,24 @@
}
private void updateExitInfo(ProcessRecord app) {
- ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecordLocked(app);
+ ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app);
mAppExitInfoTracker.handleNoteProcessDiedLocked(raw);
- mAppExitInfoTracker.recycleRawRecordLocked(raw);
+ mAppExitInfoTracker.recycleRawRecord(raw);
}
private void noteAppKill(ProcessRecord app, int reason, int subReason, String msg) {
- ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecordLocked(app);
+ ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app);
raw.setReason(reason);
raw.setSubReason(subReason);
raw.setDescription(msg);
mAppExitInfoTracker.handleNoteAppKillLocked(raw);
- mAppExitInfoTracker.recycleRawRecordLocked(raw);
+ mAppExitInfoTracker.recycleRawRecord(raw);
}
@Test
public void testApplicationExitInfo() throws Exception {
mAppExitInfoTracker.clearProcessExitInfo(true);
- mAppExitInfoTracker.mAppExitInfoLoaded = true;
+ mAppExitInfoTracker.mAppExitInfoLoaded.set(true);
mAppExitInfoTracker.mProcExitStoreDir = new File(mContext.getFilesDir(),
AppExitInfoTracker.APP_EXIT_STORE_DIR);
assertTrue(FileUtils.createDir(mAppExitInfoTracker.mProcExitStoreDir));
@@ -1001,9 +1001,9 @@
}
app.connectionGroup = connectionGroup;
app.setProcState = procState;
- app.lastMemInfo = spy(new Debug.MemoryInfo());
- app.lastPss = pss;
- app.mLastRss = rss;
+ app.mProfile.setLastMemInfo(spy(new Debug.MemoryInfo()));
+ app.mProfile.setLastPss(pss);
+ app.mProfile.setLastRss(rss);
return app;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 3710396..c82db73 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -84,7 +84,7 @@
private int mNextPackageName = 1;
private TestExecutor mExecutor = new TestExecutor();
- private CacheOomRanker mCacheOomRanker = new CacheOomRanker();
+ private CacheOomRanker mCacheOomRanker;
@Before
public void setUp() {
@@ -107,6 +107,7 @@
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+ mCacheOomRanker = new CacheOomRanker(mAms);
mCacheOomRanker.init(mExecutor);
}
@@ -383,7 +384,7 @@
app.setProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
app.setAdj = setAdj;
app.lastActivityTime = lastActivityTime;
- app.mLastRss = lastRss;
+ app.mProfile.setLastRss(lastRss);
app.setCached(false);
for (int i = 0; i < returnedToCacheCount; ++i) {
app.setCached(false);
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 8011ec2..7daf357 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -103,6 +103,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Test class for {@link OomAdjuster}.
@@ -159,8 +160,9 @@
sContext.getMainThreadHandler());
setFieldValue(ActivityManagerService.class, sService, "mContext",
sContext);
- ProcessList pr = new ProcessList();
- pr.init(sService, new ActiveUids(sService, false), null);
+ ProcessList pr = spy(new ProcessList());
+ pr.mService = sService;
+ AppProfiler profiler = mock(AppProfiler.class);
setFieldValue(ActivityManagerService.class, sService, "mProcessList",
pr);
setFieldValue(ActivityManagerService.class, sService, "mHandler",
@@ -173,13 +175,14 @@
mock(OomAdjProfiler.class));
setFieldValue(ActivityManagerService.class, sService, "mUserController",
mock(UserController.class));
- setFieldValue(ActivityManagerService.class, sService, "mAppProfiler",
- mock(AppProfiler.class));
- doReturn(new ActivityManagerService.ProcessChangeItem()).when(sService)
+ setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
+ 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));
sService.mOomAdjuster.mAdjSeq = 10000;
+ sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
}
@AfterClass
@@ -206,9 +209,9 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.maxAdj = PERSISTENT_PROC_ADJ;
app.setHasTopUi(true);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_ASLEEP;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERSISTENT_PROC_ADJ,
SCHED_GROUP_RESTRICTED);
@@ -221,7 +224,7 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.maxAdj = PERSISTENT_PROC_ADJ;
app.setHasTopUi(true);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ,
@@ -234,10 +237,10 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.maxAdj = PERSISTENT_PROC_ADJ;
- doReturn(app).when(sService).getTopAppLocked();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ doReturn(app).when(sService).getTopApp();
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doReturn(null).when(sService).getTopAppLocked();
+ doReturn(null).when(sService).getTopApp();
assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ,
SCHED_GROUP_TOP_APP);
@@ -249,10 +252,10 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(app).when(sService).getTopAppLocked();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ doReturn(app).when(sService).getTopApp();
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doReturn(null).when(sService).getTopAppLocked();
+ doReturn(null).when(sService).getTopApp();
assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_TOP_APP);
}
@@ -264,7 +267,7 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState();
app.runningRemoteAnimation = true;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
@@ -277,7 +280,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
doReturn(mock(ActiveInstrumentation.class)).when(app).getActiveInstrumentation();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doCallRealMethod().when(app).getActiveInstrumentation();
@@ -292,7 +295,7 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
doReturn(true).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class),
any(ArraySet.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(false).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class),
any(ArraySet.class));
@@ -306,7 +309,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.executingServices.add(mock(ServiceRecord.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_SERVICE, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -318,12 +321,12 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState();
- doReturn(app).when(sService).getTopAppLocked();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_ASLEEP;
+ doReturn(app).when(sService).getTopApp();
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doReturn(null).when(sService).getTopAppLocked();
+ doReturn(null).when(sService).getTopApp();
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, FOREGROUND_APP_ADJ,
SCHED_GROUP_BACKGROUND);
@@ -335,8 +338,8 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.setCurRawAdj(CACHED_APP_MIN_ADJ);
- doReturn(null).when(sService).getTopAppLocked();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ doReturn(null).when(sService).getTopApp();
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ,
@@ -363,7 +366,7 @@
return 0;
})).when(wpc).computeOomAdjFromActivities(
any(WindowProcessController.ComputeOomAdjCallback.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doCallRealMethod().when(app).getWindowProcessController();
@@ -379,7 +382,7 @@
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasRecentTasks();
app.lastTopTime = SystemClock.uptimeMillis();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doCallRealMethod().when(wpc).hasRecentTasks();
@@ -392,7 +395,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -405,7 +408,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.setHasForegroundServices(true, 0);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -418,7 +421,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.setHasOverlayUi(true);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, PERCEPTIBLE_APP_ADJ,
@@ -432,7 +435,7 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.setHasForegroundServices(true, 0);
app.lastTopTime = SystemClock.uptimeMillis();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE,
@@ -445,7 +448,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.forcingToImportant = new Object();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -460,7 +463,7 @@
doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isHeavyWeightProcess();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(false).when(wpc).isHeavyWeightProcess();
@@ -476,7 +479,7 @@
doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_HOME, HOME_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -491,7 +494,7 @@
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isPreviousProcess();
doReturn(true).when(wpc).hasActivities();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
@@ -506,7 +509,7 @@
BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
backupTarget.app = app;
doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt());
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(null).when(sService.mBackupTargets).get(anyInt());
@@ -520,7 +523,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
doReturn(true).when(app).hasClientActivities();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.setProcState);
@@ -532,7 +535,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.treatLikeActivity = true;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.setProcState);
@@ -549,7 +552,7 @@
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
app.startService(s);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_B_ADJ, SCHED_GROUP_BACKGROUND);
@@ -561,7 +564,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
app.maxAdj = PERCEPTIBLE_LOW_APP_ADJ;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, PERCEPTIBLE_LOW_APP_ADJ,
@@ -575,8 +578,8 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
app.setCached(false);
app.setCurRawAdj(SERVICE_ADJ);
- doReturn(null).when(sService).getTopAppLocked();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ doReturn(null).when(sService).getTopApp();
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE);
assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.setAdj);
@@ -593,7 +596,7 @@
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
app.startService(s);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
@@ -609,11 +612,11 @@
ServiceRecord s = bindService(app, client, null, Context.BIND_WAIVE_PRIORITY,
mock(IBinder.class));
s.startRequested = true;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client).when(sService).getTopAppLocked();
+ doReturn(client).when(sService).getTopApp();
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doReturn(null).when(sService).getTopAppLocked();
+ doReturn(null).when(sService).getTopApp();
assertProcStates(app, PROCESS_STATE_SERVICE, UNKNOWN_ADJ, SCHED_GROUP_BACKGROUND);
}
@@ -627,7 +630,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_WAIVE_PRIORITY
| Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.setProcState);
@@ -647,7 +650,7 @@
setFieldValue(ConnectionRecord.class, cr, "activity",
mock(ActivityServiceConnectionsHolder.class));
doReturn(true).when(cr.activity).isActivityVisible();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(FOREGROUND_APP_ADJ, app.setAdj);
@@ -660,7 +663,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
bindService(app, app, null, 0, mock(IBinder.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, UNKNOWN_ADJ, SCHED_GROUP_BACKGROUND);
@@ -675,7 +678,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
client.treatLikeActivity = true;
bindService(app, client, null, 0, mock(IBinder.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PROCESS_STATE_CACHED_EMPTY, app.setProcState);
@@ -695,10 +698,10 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_ALLOW_OOM_MANAGEMENT, mock(IBinder.class));
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client).when(sService).getTopAppLocked();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ doReturn(client).when(sService).getTopApp();
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doReturn(null).when(sService).getTopAppLocked();
+ doReturn(null).when(sService).getTopApp();
assertEquals(PREVIOUS_APP_ADJ, app.setAdj);
}
@@ -713,7 +716,7 @@
bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
client.maxAdj = PERSISTENT_PROC_ADJ;
client.setHasTopUi(true);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
@@ -729,7 +732,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_IMPORTANT, mock(IBinder.class));
client.executingServices.add(mock(ServiceRecord.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(FOREGROUND_APP_ADJ, app.setAdj);
@@ -744,10 +747,10 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, 0, mock(IBinder.class));
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client).when(sService).getTopAppLocked();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ doReturn(client).when(sService).getTopApp();
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doReturn(null).when(sService).getTopAppLocked();
+ doReturn(null).when(sService).getTopApp();
assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT);
}
@@ -761,7 +764,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
client.maxAdj = PERSISTENT_PROC_ADJ;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.setProcState);
@@ -776,7 +779,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class));
client.maxAdj = PERSISTENT_PROC_ADJ;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.setProcState);
@@ -791,7 +794,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, 0, mock(IBinder.class));
client.setHasForegroundServices(true, 0);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, app.setProcState);
@@ -808,7 +811,7 @@
BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
backupTarget.app = client;
doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt());
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(null).when(sService.mBackupTargets).get(anyInt());
@@ -829,7 +832,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
client.runningRemoteAnimation = true;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.setAdj);
@@ -844,7 +847,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class));
client.runningRemoteAnimation = true;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PERCEPTIBLE_APP_ADJ, app.setAdj);
@@ -859,7 +862,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, 0, mock(IBinder.class));
client.setHasOverlayUi(true);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PERCEPTIBLE_APP_ADJ, app.setAdj);
@@ -874,7 +877,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, 0, mock(IBinder.class));
client.runningRemoteAnimation = true;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(VISIBLE_APP_ADJ, app.setAdj);
@@ -889,7 +892,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_IMPORTANT_BACKGROUND, mock(IBinder.class));
client.setHasOverlayUi(true);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.setProcState);
@@ -915,7 +918,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindProvider(app, client, null, null, false);
client.treatLikeActivity = true;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, UNKNOWN_ADJ, SCHED_GROUP_BACKGROUND);
@@ -930,10 +933,10 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindProvider(app, client, null, null, false);
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client).when(sService).getTopAppLocked();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ doReturn(client).when(sService).getTopApp();
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doReturn(null).when(sService).getTopAppLocked();
+ doReturn(null).when(sService).getTopApp();
assertProcStates(app, PROCESS_STATE_BOUND_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT);
}
@@ -947,7 +950,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
client.setHasForegroundServices(true, 0);
bindProvider(app, client, null, null, false);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -962,7 +965,7 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindProvider(app, client, null, null, true);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, FOREGROUND_APP_ADJ,
@@ -975,7 +978,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
app.lastProviderTime = SystemClock.uptimeMillis();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
@@ -994,10 +997,10 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, 0, mock(IBinder.class));
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client2).when(sService).getTopAppLocked();
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ doReturn(client2).when(sService).getTopApp();
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doReturn(null).when(sService).getTopAppLocked();
+ doReturn(null).when(sService).getTopApp();
assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ,
SCHED_GROUP_DEFAULT);
@@ -1015,7 +1018,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(app, client2, null, 0, mock(IBinder.class));
client2.setHasForegroundServices(true, 0);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1034,7 +1037,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, 0, mock(IBinder.class));
client2.setHasForegroundServices(true, 0);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1059,7 +1062,7 @@
lru.add(app);
lru.add(client);
lru.add(client2);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, true, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1070,7 +1073,7 @@
SCHED_GROUP_DEFAULT);
client2.setHasForegroundServices(false, 0);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(client2, true, OomAdjuster.OOM_ADJ_REASON_NONE);
assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.setProcState);
@@ -1096,7 +1099,7 @@
lru.add(app);
lru.add(client);
lru.add(client2);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, true, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1124,7 +1127,7 @@
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.forcingToImportant = new Object();
bindService(app, client3, null, 0, mock(IBinder.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1150,7 +1153,7 @@
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.forcingToImportant = new Object();
bindService(app, client3, null, 0, mock(IBinder.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -1178,7 +1181,7 @@
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
client4.forcingToImportant = new Object();
bindService(app, client4, null, 0, mock(IBinder.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -1208,7 +1211,7 @@
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
client4.setHasForegroundServices(true, 0);
bindService(app, client4, null, 0, mock(IBinder.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1234,7 +1237,7 @@
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.forcingToImportant = new Object();
bindService(app, client3, null, 0, mock(IBinder.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1253,7 +1256,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
client2.setHasForegroundServices(true, 0);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1273,7 +1276,7 @@
bindProvider(client, client2, null, null, false);
client2.setHasForegroundServices(true, 0);
bindService(client2, app, null, 0, mock(IBinder.class));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1292,7 +1295,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
client2.setHasForegroundServices(true, 0);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1312,7 +1315,7 @@
bindProvider(client, client2, null, null, false);
client2.setHasForegroundServices(true, 0);
bindProvider(client2, app, null, null, false);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1332,7 +1335,7 @@
lru.clear();
lru.add(app);
lru.add(app2);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
lru.clear();
@@ -1356,7 +1359,7 @@
lru.clear();
lru.add(app);
lru.add(app2);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
lru.clear();
@@ -1384,7 +1387,7 @@
lru.add(app);
lru.add(app2);
lru.add(app3);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
lru.clear();
@@ -1435,7 +1438,7 @@
lru.add(app3);
lru.add(app4);
lru.add(app5);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
lru.clear();
@@ -1481,7 +1484,7 @@
lru.add(app3);
lru.add(app2);
lru.add(app);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
lru.clear();
@@ -1527,7 +1530,7 @@
lru.add(app2);
lru.add(app);
lru.add(app5);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
lru.clear();
@@ -1573,7 +1576,7 @@
lru.add(app3);
lru.add(app4);
lru.add(app5);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
lru.clear();
@@ -1618,7 +1621,7 @@
lru.add(app3);
lru.add(app2);
lru.add(app);
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.mNumServiceProcs = 3;
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
lru.clear();
@@ -1678,7 +1681,7 @@
app2.startService(s2);
app2.hasShownUi = false;
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
@@ -1773,11 +1776,12 @@
ai.longVersionCode = versionCode;
ai.targetSdkVersion = targetSdkVersion;
ProcessRecord app = new ProcessRecord(service, ai, processName, uid);
+ final ProcessProfileRecord profile = app.mProfile;
app.thread = mock(IApplicationThread.class);
app.lastActivityTime = lastActivityTime;
- app.lastPssTime = lastPssTime;
- app.nextPssTime = nextPssTime;
- app.lastPss = lastPss;
+ profile.setLastPssTime(lastPssTime);
+ profile.setNextPssTime(nextPssTime);
+ profile.setLastPss(lastPss);
app.maxAdj = maxAdj;
app.setRawAdj = setRawAdj;
app.curAdj = curAdj;
diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java
index c9fcd02..c5e1ed1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java
@@ -37,6 +37,7 @@
import com.android.server.LocalServices;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,6 +83,13 @@
mService.onStart();
}
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
@Test
public void testUsageEventListener() throws Exception {
TestUsageEventListener listener = new TestUsageEventListener();
diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
index 0a35db5..f7b2492 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
@@ -16,34 +16,61 @@
package com.android.server;
-import static com.android.server.testutils.TestUtils.assertExpectException;
-
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+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.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManagerInternal;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
import android.hardware.vibrator.IVibrator;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
import android.os.CombinedVibrationEffect;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.IVibratorStateListener;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.server.vibrator.FakeVibrator;
import com.android.server.vibrator.FakeVibratorControllerProvider;
import com.android.server.vibrator.VibratorController;
@@ -51,12 +78,16 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.Predicate;
/**
* Tests for {@link VibratorManagerService}.
@@ -67,39 +98,86 @@
@Presubmit
public class VibratorManagerServiceTest {
+ private static final int TEST_TIMEOUT_MILLIS = 1_000;
private static final int UID = Process.ROOT_UID;
private static final String PACKAGE_NAME = "package";
+ private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
+ private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
+ .setBatterySaverEnabled(true).build();
private static final VibrationAttributes ALARM_ATTRS =
new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
+ private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_TOUCH).build();
+ private static final VibrationAttributes NOTIFICATION_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_NOTIFICATION).build();
+ private static final VibrationAttributes RINGTONE_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_RINGTONE).build();
@Rule public MockitoRule rule = MockitoJUnit.rule();
+ @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock;
+ @Mock private PackageManagerInternal mPackageManagerInternalMock;
@Mock private PowerManagerInternal mPowerManagerInternalMock;
@Mock private PowerSaveState mPowerSaveStateMock;
+ @Mock private AppOpsManager mAppOpsManagerMock;
+ @Mock private IInputManager mIInputManagerMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
+ private Context mContextSpy;
private TestLooper mTestLooper;
+ private FakeVibrator mVibrator;
+ private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
@Before
public void setUp() throws Exception {
mTestLooper = new TestLooper();
+ mVibrator = new FakeVibrator();
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+ InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
+ ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+ when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+ when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibrator);
+ when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
+ when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock);
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
+ when(mPackageManagerInternalMock.getSystemUiServiceComponent())
+ .thenReturn(new ComponentName("", ""));
when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION))
.thenReturn(mPowerSaveStateMock);
+ doAnswer(invocation -> {
+ mRegisteredPowerModeListener = invocation.getArgument(0);
+ return null;
+ }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
+
+ addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock);
addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
+
+ mTestLooper.startAutoDispatch();
}
@After
public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
}
private VibratorManagerService createService() {
VibratorManagerService service = new VibratorManagerService(
- InstrumentationRegistry.getContext(),
+ mContextSpy,
new VibratorManagerService.Injector() {
@Override
VibratorManagerService.NativeWrapper getNativeWrapper() {
@@ -172,6 +250,74 @@
}
@Test
+ public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createService();
+ IVibratorStateListener listenerMock = mockVibratorStateListener();
+ service.registerVibratorStateListener(1, listenerMock);
+
+ vibrate(service, VibrationEffect.createOneShot(40, 100), ALARM_ATTRS);
+ // Wait until service knows vibrator is on.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ // Wait until effect ends.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ InOrder inOrderVerifier = inOrder(listenerMock);
+ // First notification done when listener is registered.
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
+ inOrderVerifier.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createService();
+ IVibratorStateListener listenerMock = mockVibratorStateListener();
+ service.registerVibratorStateListener(1, listenerMock);
+
+ vibrate(service, VibrationEffect.createOneShot(40, 100), ALARM_ATTRS);
+
+ // Wait until service knows vibrator is on.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ service.unregisterVibratorStateListener(1, listenerMock);
+
+ // Wait until vibrator is off.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ InOrder inOrderVerifier = inOrder(listenerMock);
+ // First notification done when listener is registered.
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
+ inOrderVerifier.verify(listenerMock, atLeastOnce()).asBinder(); // unregister
+ inOrderVerifier.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void registerVibratorStateListener_multipleVibratorsAreTriggered() throws Exception {
+ mockVibrators(0, 1, 2);
+ VibratorManagerService service = createService();
+ IVibratorStateListener[] listeners = new IVibratorStateListener[3];
+ for (int i = 0; i < 3; i++) {
+ listeners[i] = mockVibratorStateListener();
+ service.registerVibratorStateListener(i, listeners[i]);
+ }
+
+ vibrate(service, CombinedVibrationEffect.startSynced()
+ .addVibrator(0, VibrationEffect.createOneShot(40, 100))
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .combine(), ALARM_ATTRS);
+ // Wait until service knows vibrator is on.
+ assertTrue(waitUntil(s -> s.isVibrating(0), service, TEST_TIMEOUT_MILLIS));
+
+ verify(listeners[0]).onVibrating(eq(true));
+ verify(listeners[1]).onVibrating(eq(true));
+ verify(listeners[2], never()).onVibrating(eq(true));
+ }
+
+ @Test
public void setAlwaysOnEffect_withMono_enablesAlwaysOnEffectToAllVibratorsWithCapability() {
mockVibrators(1, 2, 3);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
@@ -276,22 +422,264 @@
}
@Test
- public void vibrate_isUnsupported() {
+ public void vibrate_withRingtone_usesRingtoneSettings() throws Exception {
+ mockVibrators(1);
+ mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+ setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
VibratorManagerService service = createService();
- CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
- assertExpectException(UnsupportedOperationException.class,
- "Not implemented",
- () -> service.vibrate(UID, PACKAGE_NAME, effect, ALARM_ATTRS, "reason", service));
+ vibrate(service, VibrationEffect.createOneShot(40, 1), RINGTONE_ATTRS);
+
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+ setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
+ service = createService();
+ vibrate(service, VibrationEffect.createOneShot(40, 10), RINGTONE_ATTRS);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+ setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ service = createService();
+ vibrate(service, VibrationEffect.createOneShot(40, 100), RINGTONE_ATTRS);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ assertEquals(2, mVibratorProviders.get(1).getEffects().size());
+ assertEquals(Arrays.asList(10, 100), mVibratorProviders.get(1).getAmplitudes());
}
@Test
- public void cancelVibrate_isUnsupported() {
+ public void vibrate_withPowerMode_usesPowerModeState() throws Exception {
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createService();
- CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
- assertExpectException(UnsupportedOperationException.class,
- "Not implemented", () -> service.cancelVibrate(service));
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+ vibrate(service, VibrationEffect.createOneShot(1, 1), HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 1,
+ service, TEST_TIMEOUT_MILLIS));
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
+ vibrate(service, VibrationEffect.createOneShot(3, 3), /* attributes= */ null);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 2,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, VibrationEffect.createOneShot(4, 4), NOTIFICATION_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 3,
+ service, TEST_TIMEOUT_MILLIS));
+
+ assertEquals(Arrays.asList(2, 3, 4), fakeVibrator.getAmplitudes());
+ }
+
+ @Test
+ public void vibrate_withAudioAttributes_usesOriginalAudioUsageInAppOpsManager() {
+ VibratorManagerService service = createService();
+
+ VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ AudioAttributes audioAttributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY).build();
+ VibrationAttributes vibrationAttributes = new VibrationAttributes.Builder(
+ audioAttributes, effect).build();
+
+ vibrate(service, effect, vibrationAttributes);
+
+ verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY), anyInt(), anyString());
+ }
+
+ @Test
+ public void vibrate_withVibrationAttributes_usesCorrespondingAudioUsageInAppOpsManager() {
+ VibratorManagerService service = createService();
+
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), NOTIFICATION_ATTRS);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_COMMUNICATION_REQUEST).build());
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_UNKNOWN).build());
+
+ InOrder inOrderVerifier = inOrder(mAppOpsManagerMock);
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ALARM), anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_NOTIFICATION), anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_NOTIFICATION_RINGTONE), anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION), anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST),
+ anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
+ }
+
+ @Test
+ public void vibrate_withInputDevices_vibratesInputDevices() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
+ when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
+ setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
+ VibratorManagerService service = createService();
+
+ // Prebaked vibration will play fallback waveform on input device.
+ ArgumentCaptor<VibrationEffect> captor = ArgumentCaptor.forClass(VibrationEffect.class);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
+ verify(mIInputManagerMock).vibrate(eq(1), captor.capture(), any());
+ assertTrue(captor.getValue() instanceof VibrationEffect.Waveform);
+
+ VibrationEffect[] effects = new VibrationEffect[]{
+ VibrationEffect.createOneShot(100, 128),
+ VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1),
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose(),
+ };
+
+ for (VibrationEffect effect : effects) {
+ vibrate(service, effect, ALARM_ATTRS);
+ verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
+ }
+
+ // VibrationThread will start this vibration async, so wait before checking it never played.
+ assertFalse(waitUntil(s -> !mVibratorProviders.get(1).getEffects().isEmpty(),
+ service, /* timeout= */ 50));
+ }
+
+ @Test
+ public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
+ IVibrator.CAP_AMPLITUDE_CONTROL);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createService();
+ // The native callback will be dispatched manually in this test.
+ mTestLooper.stopAutoDispatchAndIgnoreExceptions();
+
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait before triggering callbacks.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ // Trigger callbacks from controller.
+ mTestLooper.moveTimeForward(50);
+ mTestLooper.dispatchAll();
+
+ // VibrationThread needs some time to react to native callbacks and stop the vibrator.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ }
+
+
+ @Test
+ public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
+ mVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
+ IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createService();
+
+ vibrate(service, CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .combine(), ALARM_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 1,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.createOneShot(20, 100))
+ .combine(), NOTIFICATION_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 2,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .compose(), HAPTIC_FEEDBACK_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 3,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
+
+ assertEquals(3, fakeVibrator.getEffects().size());
+ assertEquals(1, fakeVibrator.getAmplitudes().size());
+
+ // Alarm vibration is always VIBRATION_INTENSITY_HIGH.
+ VibrationEffect expected = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, false,
+ VibrationEffect.EFFECT_STRENGTH_STRONG);
+ assertEquals(expected, fakeVibrator.getEffects().get(0));
+
+ // Notification vibrations will be scaled with SCALE_VERY_HIGH.
+ assertTrue(150 < fakeVibrator.getAmplitudes().get(0));
+
+ // Haptic feedback vibrations will be scaled with SCALE_LOW.
+ VibrationEffect.Composed played =
+ (VibrationEffect.Composed) fakeVibrator.getEffects().get(2);
+ assertTrue(0.5 < played.getPrimitiveEffects().get(0).scale);
+ assertTrue(0.5 > played.getPrimitiveEffects().get(1).scale);
+
+ // Ring vibrations have intensity OFF and are not played.
+ }
+
+ @Test
+ public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception {
+ mockVibrators(1, 2);
+ VibratorManagerService service = createService();
+ vibrate(service,
+ CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
+ .combine(),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+ // Haptic feedback cancelled on low power mode.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ }
+
+ @Test
+ public void vibrate_withSettingsChange_doNotCancelVibration() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createService();
+
+ vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ service.updateServiceState();
+ // Vibration is not stopped nearly after updating service.
+ assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
+ }
+
+ @Test
+ public void cancelVibrate_stopsVibrating() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createService();
+
+ service.cancelVibrate(service);
+ assertFalse(service.isVibrating(1));
+
+ vibrate(service, VibrationEffect.createOneShot(10_000, 100), ALARM_ATTRS);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ service.cancelVibrate(service);
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
}
private void mockVibrators(int... vibratorIds) {
@@ -302,8 +690,56 @@
when(mNativeWrapperMock.getVibratorIds()).thenReturn(vibratorIds);
}
+ private IVibratorStateListener mockVibratorStateListener() {
+ IVibratorStateListener listenerMock = mock(IVibratorStateListener.class);
+ IBinder binderMock = mock(IBinder.class);
+ when(listenerMock.asBinder()).thenReturn(binderMock);
+ return listenerMock;
+ }
+
+ private InputDevice createInputDeviceWithVibrator(int id) {
+ return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
+ null, /* hasVibrator= */ true, false, false, false, false);
+ }
+
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
}
+
+ private void setRingerMode(int ringerMode) {
+ AudioManager audioManager = mContextSpy.getSystemService(AudioManager.class);
+ audioManager.setRingerModeInternal(ringerMode);
+ assertEquals(ringerMode, audioManager.getRingerModeInternal());
+ }
+
+ private void setUserSetting(String settingName, int value) {
+ Settings.System.putIntForUser(
+ mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+ }
+
+ private void setGlobalSetting(String settingName, int value) {
+ Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
+ }
+
+ private void vibrate(VibratorManagerService service, VibrationEffect effect,
+ VibrationAttributes attrs) {
+ vibrate(service, CombinedVibrationEffect.createSynced(effect), attrs);
+ }
+
+ private void vibrate(VibratorManagerService service, CombinedVibrationEffect effect,
+ VibrationAttributes attrs) {
+ service.vibrate(UID, PACKAGE_NAME, effect, attrs, "some reason", service);
+ }
+
+ private boolean waitUntil(Predicate<VibratorManagerService> predicate,
+ VibratorManagerService service, long timeout) throws InterruptedException {
+ long timeoutTimestamp = SystemClock.uptimeMillis() + timeout;
+ boolean predicateResult = false;
+ while (!predicateResult && SystemClock.uptimeMillis() < timeoutTimestamp) {
+ Thread.sleep(10);
+ predicateResult = predicate.test(service);
+ }
+ return predicateResult;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 743848c..2a7905a 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -582,16 +582,19 @@
VibratorService service = createService();
service.registerVibratorStateListener(mVibratorStateListenerMock);
- verify(mVibratorStateListenerMock).onVibrating(false);
- vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
+ vibrate(service, VibrationEffect.createOneShot(30, 100), ALARM_ATTRS);
// VibrationThread will start this vibration async, so wait before triggering callbacks.
Thread.sleep(10);
+ assertTrue(service.isVibrating());
+
service.unregisterVibratorStateListener(mVibratorStateListenerMock);
// Trigger callbacks from controller.
- mTestLooper.moveTimeForward(150);
+ mTestLooper.moveTimeForward(50);
mTestLooper.dispatchAll();
+ Thread.sleep(20);
+ assertFalse(service.isVibrating());
InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
// First notification done when listener is registered.
@@ -745,7 +748,8 @@
private InputDevice createInputDeviceWithVibrator(int id) {
return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, /* hasVibrator= */ true, false, false, false /* hasSensor */);
+ null, /* hasVibrator= */ true, false, false, false /* hasSensor */,
+ false /* hasBattery */);
}
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index df8a720..110bb21 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -164,9 +164,7 @@
@SmallTest
public void testRegisterSystemActionWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
- .enforceCallerIsRecentsOrHasPermission(
- Manifest.permission.MANAGE_ACCESSIBILITY,
- AccessibilityManagerService.FUNCTION_REGISTER_SYSTEM_ACTION);
+ .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
try {
mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
@@ -185,9 +183,7 @@
@SmallTest
public void testUnregisterSystemActionWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
- .enforceCallerIsRecentsOrHasPermission(
- Manifest.permission.MANAGE_ACCESSIBILITY,
- AccessibilityManagerService.FUNCTION_UNREGISTER_SYSTEM_ACTION);
+ .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
try {
mA11yms.unregisterSystemAction(ACTION_ID);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index cc8ac86..c7e7c78 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -51,9 +51,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
-import com.android.server.LocalServices;
-import com.android.server.wm.ActivityTaskManagerInternal;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -123,7 +120,6 @@
@Mock private AccessibilityWindowManager mMockA11yWindowManager;
@Mock private AppWidgetManagerInternal mMockAppWidgetManager;
@Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
- @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
@Before
public void setUp() {
@@ -132,10 +128,6 @@
when(mMockContext.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager);
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager);
- LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
- LocalServices.addService(
- ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
-
mA11ySecurityPolicy = new AccessibilitySecurityPolicy(mMockContext, mMockA11yUserManager);
mA11ySecurityPolicy.setAccessibilityWindowManager(mMockA11yWindowManager);
mA11ySecurityPolicy.setAppWidgetManager(mMockAppWidgetManager);
@@ -570,10 +562,4 @@
APP_UID, PACKAGE_NAME);
}
- @Test
- public void testEnforceCallerIsRecentsOrHasPermission() {
- mA11ySecurityPolicy.enforceCallerIsRecentsOrHasPermission(PERMISSION, FUNCTION);
- verify(mMockActivityTaskManagerInternal).enforceCallerIsRecentsOrHasPermission(
- PERMISSION, FUNCTION);
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
new file mode 100644
index 0000000..fdf5095
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.hardware.power.stats.Channel;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
+import android.power.PowerStatsInternal;
+import android.util.SparseArray;
+import android.util.SparseLongArray;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.power.MeasuredEnergyArray;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Tests for {@link BatteryExternalStatsWorker}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:BatteryExternalStatsWorkerTest
+ */
+public class BatteryExternalStatsWorkerTest {
+ private BatteryExternalStatsWorker mBatteryExternalStatsWorker;
+ private TestBatteryStatsImpl mBatteryStatsImpl;
+ private TestPowerStatsInternal mPowerStatsInternal;
+
+ @Before
+ public void setUp() {
+ final Context context = InstrumentationRegistry.getContext();
+
+ mBatteryStatsImpl = new TestBatteryStatsImpl();
+ mPowerStatsInternal = new TestPowerStatsInternal();
+ mBatteryExternalStatsWorker = new BatteryExternalStatsWorker(new TestInjector(context),
+ mBatteryStatsImpl);
+ }
+
+ @Test
+ public void getEnergyConsumptionData() {
+ SparseLongArray expectSubsystems = new SparseLongArray();
+ // Add some energy consumers used by BatteryExternalStatsWorker.
+ final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0,
+ "display");
+ mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345);
+ expectSubsystems.put(MeasuredEnergyArray.SUBSYSTEM_DISPLAY, 12345);
+
+ // Add an arbitrary energy consumer unused by BatteryExternalStatsWorker.
+ // Must be changed if '154' ever becomes an EnergyConsumerType used by BESW.
+ final int someId = mPowerStatsInternal.addEnergyConsumer((byte) 154, 0, "some_consumer");
+ mPowerStatsInternal.incrementEnergyConsumption(someId, 34567);
+
+ // Inform BESW that PowerStatsInternal is ready to query
+ mBatteryExternalStatsWorker.systemServicesReady();
+
+ MeasuredEnergyArray energies = mBatteryExternalStatsWorker.getEnergyConsumptionData();
+
+ assertEquals(expectSubsystems.size(), energies.size());
+ final int size = expectSubsystems.size();
+
+ for (int i = 0; i < size; i++) {
+ int subsystem = expectSubsystems.keyAt(i);
+ // find the subsystem in the returned MeasuredEnergyArray
+ int subsystemIndex = -1;
+ for (int j = 0; j < size; j++) {
+ if (subsystem == energies.getSubsystem(i)) {
+ subsystemIndex = i;
+ break;
+ }
+ }
+ assertNotEquals("Subsystem " + subsystem + " not found in MeasuredEnergyArray", -1,
+ subsystemIndex);
+ assertEquals(expectSubsystems.valueAt(i), energies.getEnergy(subsystemIndex));
+ }
+ }
+
+ public class TestInjector extends BatteryExternalStatsWorker.Injector {
+ public TestInjector(Context context) {
+ super(context);
+ }
+
+ public <T> T getSystemService(Class<T> serviceClass) {
+ return null;
+ }
+
+ public <T> T getLocalService(Class<T> serviceClass) {
+ if (serviceClass == PowerStatsInternal.class) {
+ return (T) mPowerStatsInternal;
+ }
+ return null;
+ }
+ }
+
+ public class TestBatteryStatsImpl extends BatteryStatsImpl {
+ }
+
+ public class TestPowerStatsInternal extends PowerStatsInternal {
+ private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray<>();
+ private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults =
+ new SparseArray<>();
+ private final int mTimeSinceBoot = 0;
+
+ @Override
+ public EnergyConsumer[] getEnergyConsumerInfo() {
+ final int size = mEnergyConsumers.size();
+ final EnergyConsumer[] consumers = new EnergyConsumer[size];
+ for (int i = 0; i < size; i++) {
+ consumers[i] = mEnergyConsumers.valueAt(i);
+ }
+ return consumers;
+ }
+
+ @Override
+ public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
+ int[] energyConsumerIds) {
+ final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture();
+ final int size = mEnergyConsumerResults.size();
+ final EnergyConsumerResult[] results = new EnergyConsumerResult[size];
+ for (int i = 0; i < size; i++) {
+ results[i] = mEnergyConsumerResults.valueAt(i);
+ }
+ future.complete(results);
+ return future;
+ }
+
+ @Override
+ public PowerEntity[] getPowerEntityInfo() {
+ return new PowerEntity[0];
+ }
+
+ @Override
+ public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+ int[] powerEntityIds) {
+ return new CompletableFuture<>();
+ }
+
+ @Override
+ public Channel[] getEnergyMeterInfo() {
+ return new Channel[0];
+ }
+
+ @Override
+ public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+ int[] channelIds) {
+ return new CompletableFuture<>();
+ }
+
+ /**
+ * Util method to add a new EnergyConsumer for testing
+ *
+ * @return the EnergyConsumer id of the new EnergyConsumer
+ */
+ public int addEnergyConsumer(@EnergyConsumerType byte type, int ordinal, String name) {
+ final EnergyConsumer consumer = new EnergyConsumer();
+ final int id = getNextAvailableId();
+ consumer.id = id;
+ consumer.type = type;
+ consumer.ordinal = ordinal;
+ consumer.name = name;
+ mEnergyConsumers.put(id, consumer);
+
+ final EnergyConsumerResult result = new EnergyConsumerResult();
+ result.id = id;
+ result.timestampMs = mTimeSinceBoot;
+ result.energyUWs = 0;
+ mEnergyConsumerResults.put(id, result);
+ return id;
+ }
+
+ public void incrementEnergyConsumption(int id, long energyUWs) {
+ EnergyConsumerResult result = mEnergyConsumerResults.get(id, null);
+ assertNotNull(result);
+ result.energyUWs += energyUWs;
+ }
+
+ private int getNextAvailableId() {
+ final int size = mEnergyConsumers.size();
+ // Just return the first index that does not match the key (aka the EnergyConsumer id)
+ for (int i = size - 1; i >= 0; i--) {
+ if (mEnergyConsumers.keyAt(i) == i) return i + 1;
+ }
+ // Otherwise return the lowest id
+ return 0;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
index 4f58c87..7355b80 100644
--- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -40,6 +40,9 @@
import org.junit.BeforeClass;
import org.junit.Test;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
/**
* Test class for {@link OomAdjuster}.
*
@@ -76,6 +79,9 @@
sService.mConstants = new ActivityManagerConstants(sContext, sService,
sContext.getMainThreadHandler());
+ final AppProfiler profiler = mock(AppProfiler.class);
+ setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
+ setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null);
LocalServices.addService(UsageStatsManagerInternal.class,
mock(UsageStatsManagerInternal.class));
@@ -83,6 +89,18 @@
});
}
+ private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ Field mfield = Field.class.getDeclaredField("accessFlags");
+ mfield.setAccessible(true);
+ mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
+ field.set(obj, val);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ }
+ }
+
@AfterClass
public static void tearDownOnce() {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
index ded14b8..263efa6 100644
--- a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
@@ -42,6 +42,8 @@
import org.junit.BeforeClass;
import org.junit.Test;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.util.Collections;
/**
@@ -67,6 +69,11 @@
sService.mActivityTaskManager = new ActivityTaskManagerService(sContext);
sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
sService.mAtmInternal = sService.mActivityTaskManager.getAtmInternal();
+ final AppProfiler profiler = mock(AppProfiler.class);
+ setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
+ setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
+ final ProcessList processList = new ProcessList();
+ setFieldValue(ActivityManagerService.class, sService, "mProcessList", processList);
});
// Avoid NPE when initializing {@link ProcessRecord#mWindowProcessController}.
@@ -76,6 +83,18 @@
doReturn(sysUiName).when(packageManagerInternal).getSystemUiServiceComponent();
}
+ private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ Field mfield = Field.class.getDeclaredField("accessFlags");
+ mfield.setAccessible(true);
+ mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
+ field.set(obj, val);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ }
+ }
+
@AfterClass
public static void tearDownOnce() {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 784718b..9ffb5017 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -770,7 +770,7 @@
}
@Override
- void reportGlobalUsageEventLocked(int event) {
+ void reportGlobalUsageEvent(int event) {
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
new file mode 100644
index 0000000..738f008
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
@@ -0,0 +1,129 @@
+/*
+ * 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.app;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.content.Context;
+import android.util.AtomicFile;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class GameManagerServiceSettingsTests {
+
+ private static final String TAG = "GameServiceSettingsTests";
+ private static final String PACKAGE_NAME_1 = "com.android.app1";
+ private static final String PACKAGE_NAME_2 = "com.android.app2";
+ private static final String PACKAGE_NAME_3 = "com.android.app3";
+
+ private void writeFile(File file, byte[] data) {
+ file.mkdirs();
+ try {
+ AtomicFile aFile = new AtomicFile(file);
+ FileOutputStream fos = aFile.startWrite();
+ fos.write(data);
+ aFile.finishWrite(fos);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Cannot write file " + file.getPath());
+ }
+ }
+
+ private void writeGameServiceXml() {
+ writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
+ "system/game-manager-service.xml"),
+ ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<packages>\n"
+ + " <package name=\"com.android.app1\" gameMode=\"1\">\n"
+ + " </package>\n"
+ + " <package name=\"com.android.app2\" gameMode=\"2\">\n"
+ + " </package>\n"
+ + " <package name=\"com.android.app3\" gameMode=\"3\">\n"
+ + " </package>\n"
+ + "</packages>\n").getBytes());
+ }
+
+ private void deleteSystemFolder() {
+ File systemFolder = new File(InstrumentationRegistry.getContext().getFilesDir(), "system");
+ deleteFolder(systemFolder);
+ }
+
+ private static void deleteFolder(File folder) {
+ File[] files = folder.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ deleteFolder(file);
+ }
+ }
+ folder.delete();
+ }
+
+ private void writeOldFiles() {
+ deleteSystemFolder();
+ writeGameServiceXml();
+ }
+
+ private void verifyGameServiceSettingsData(Settings settings) {
+ assertThat(settings.getGameModeLocked(PACKAGE_NAME_1), is(1));
+ assertThat(settings.getGameModeLocked(PACKAGE_NAME_2), is(2));
+ assertThat(settings.getGameModeLocked(PACKAGE_NAME_3), is(3));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
+ }
+
+ /** read in data and verify */
+ @Test
+ public void testReadGameServiceSettings() {
+ /* write out files and read */
+ writeOldFiles();
+ final Context context = InstrumentationRegistry.getContext();
+ Settings settings = new Settings(context.getFilesDir());
+ assertThat(settings.readPersistentDataLocked(), is(true));
+ verifyGameServiceSettingsData(settings);
+ }
+
+ /** read in data, write it out, and read it back in. Verify same. */
+ @Test
+ public void testWriteGameServiceSettings() {
+ // write out files and read
+ writeOldFiles();
+ final Context context = InstrumentationRegistry.getContext();
+ Settings settings = new Settings(context.getFilesDir());
+ assertThat(settings.readPersistentDataLocked(), is(true));
+
+ // write out, read back in and verify the same
+ settings.writePersistentDataLocked();
+ assertThat(settings.readPersistentDataLocked(), is(true));
+ verifyGameServiceSettingsData(settings);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
new file mode 100644
index 0000000..caa46da
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -0,0 +1,86 @@
+/*
+ * 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.app;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.GameManager;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class GameManagerServiceTests {
+
+ private static final String TAG = "GameServiceTests";
+ private static final String PACKAGE_NAME_0 = "com.android.app0";
+ private static final String PACKAGE_NAME_1 = "com.android.app1";
+ private static final int USER_ID_1 = 1001;
+ private static final int USER_ID_2 = 1002;
+
+ /**
+ * By default game mode is not supported.
+ */
+ @Test
+ public void testGameModeDefaultValue() {
+ GameManagerService gameManagerService =
+ new GameManagerService(InstrumentationRegistry.getContext());
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ gameManagerService.getGameMode(PACKAGE_NAME_0, USER_ID_1));
+ }
+
+ /**
+ * Test the default behaviour for a nonexistent user.
+ */
+ @Test
+ public void testDefaultValueForNonexistentUser() {
+ GameManagerService gameManagerService =
+ new GameManagerService(InstrumentationRegistry.getContext());
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_2);
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_2));
+ }
+
+ /**
+ * Test getter and setter of game modes.
+ */
+ @Test
+ public void testGameMode() {
+ GameManagerService gameManagerService =
+ new GameManagerService(InstrumentationRegistry.getContext());
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+ gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+ gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE,
+ USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+ }
+}
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 d0370b6..6777e1a 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -25,7 +25,6 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
-import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
@@ -64,6 +63,8 @@
private static final int USER_ID_1 = 1;
private static final int USER_ID_2 = 2;
+ private final List<UserInfo> mUserInfos = new ArrayList<>();
+
private AppHibernationService mAppHibernationService;
private BroadcastReceiver mBroadcastReceiver;
@Mock
@@ -85,69 +86,59 @@
mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
mIActivityManager, mUserManager);
- verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any());
+ verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
mBroadcastReceiver = mReceiverCaptor.getValue();
- List<UserInfo> userList = new ArrayList<>();
- userList.add(new UserInfo(USER_ID_1, "user 1", 0 /* flags */));
- doReturn(userList).when(mUserManager).getUsers();
-
- List<PackageInfo> userPackages = new ArrayList<>();
- userPackages.add(makePackageInfo(PACKAGE_NAME_1));
-
- doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
- .getInstalledPackages(anyInt(), eq(USER_ID_1));
+ doReturn(mUserInfos).when(mUserManager).getUsers();
doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
anyInt(), anyBoolean(), anyBoolean(), any(), any());
- mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ UserInfo userInfo = addUser(USER_ID_1);
+ mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
}
@Test
- public void testSetHibernating_packageIsHibernating() {
+ public void testSetHibernatingForUser_packageIsHibernating() {
// WHEN we hibernate a package for a user
- mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
// THEN the package is marked hibernating for the user
- assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1));
+ assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
}
@Test
- public void testSetHibernating_newPackageAdded_packageIsHibernating() {
+ public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating() {
// WHEN a new package is added and it is hibernated
Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
mBroadcastReceiver.onReceive(mContext, intent);
- mAppHibernationService.setHibernating(PACKAGE_NAME_2, USER_ID_1, true);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_1, true);
// THEN the new package is hibernated
- assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_2, USER_ID_1));
+ assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_2, USER_ID_1));
}
@Test
- public void testSetHibernating_newUserAdded_packageIsHibernating() throws RemoteException {
+ public void testSetHibernatingForUser_newUserUnlocked_packageIsHibernating()
+ throws RemoteException {
// WHEN a new user is added and a package from the user is hibernated
- List<PackageInfo> userPackages = new ArrayList<>();
- userPackages.add(makePackageInfo(PACKAGE_NAME_1));
- doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
- .getInstalledPackages(anyInt(), eq(USER_ID_2));
- Intent intent = new Intent(Intent.ACTION_USER_ADDED);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2);
- mBroadcastReceiver.onReceive(mContext, intent);
-
- mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_2, true);
+ UserInfo user2 = addUser(USER_ID_2);
+ mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
// THEN the new user's package is hibernated
- assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_2));
+ assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2));
}
@Test
- public void testIsHibernating_packageReplaced_stillReturnsHibernating() {
+ public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() {
// GIVEN a package is currently hibernated
- mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
// WHEN the package is removed but marked as replacing
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
@@ -157,7 +148,38 @@
mBroadcastReceiver.onReceive(mContext, intent);
// THEN the package is still hibernating
- assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1));
+ assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernatingGlobally_packageIsHibernatingGlobally() {
+ // WHEN we hibernate a package
+ mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+
+ // THEN the package is marked hibernating for the user
+ assertTrue(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
+ }
+
+ /**
+ * Add a mock user with one package.
+ */
+ private UserInfo addUser(int userId) throws RemoteException {
+ return addUser(userId, new String[]{PACKAGE_NAME_1});
+ }
+
+ /**
+ * Add a mock user with the packages specified.
+ */
+ private UserInfo addUser(int userId, String[] packageNames) throws RemoteException {
+ UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */);
+ mUserInfos.add(userInfo);
+ List<PackageInfo> userPackages = new ArrayList<>();
+ for (String pkgName : packageNames) {
+ userPackages.add(makePackageInfo(pkgName));
+ }
+ doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
+ .getInstalledPackages(anyInt(), eq(userId));
+ return userInfo;
}
private static PackageInfo makePackageInfo(String packageName) {
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index 8d744c4..9b6c723 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -700,14 +700,14 @@
public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder()
- .addSchemaType("FakeType")
+ .addFilterSchemas("FakeType")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
searchSpec =
new SearchSpec.Builder()
- .addNamespace("FakeNamespace")
+ .addFilterNamespaces("FakeNamespace")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 57b0d01..2d45726 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -196,7 +196,8 @@
BiometricSchedulerProto bsp = getDump(true /* clearSchedulerBuffer */);
assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation);
assertEquals(0, bsp.totalOperations);
- assertEquals(0, bsp.recentOperations.length);
+ // TODO:(b/178828362) See bug and/or commit message :/
+ // assertEquals(0, bsp.recentOperations.length);
// Pretend the scheduler is busy enrolling, and check the proto dump again.
final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
@@ -207,7 +208,11 @@
assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation);
// No operations have completed yet
assertEquals(0, bsp.totalOperations);
- assertEquals(0, bsp.recentOperations.length);
+
+ // TODO:(b/178828362) See bug and/or commit message :/
+ assertEquals(1, bsp.recentOperations.length);
+ assertEquals(BiometricsProto.CM_NONE, bsp.recentOperations[0]);
+
// Finish this operation, so the next scheduled one can start
client.getCallback().onClientFinished(client, true);
}
@@ -223,7 +228,8 @@
assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation);
// No operations have completed yet
assertEquals(0, bsp.totalOperations);
- assertEquals(0, bsp.recentOperations.length);
+ // TODO:(b/178828362) See bug and/or commit message :/
+ // assertEquals(0, bsp.recentOperations.length);
// Finish this operation, so the next scheduled one can start
client.getCallback().onClientFinished(client, true);
@@ -265,7 +271,10 @@
// RecentOperations queue is cleared (by the previous dump)
bsp = getDump(true /* clearSchedulerBuffer */);
- assertEquals(0, bsp.recentOperations.length);
+
+ // TODO:(b/178828362) See bug and/or commit message :/
+ assertEquals(1, bsp.recentOperations.length);
+ assertEquals(BiometricsProto.CM_NONE, bsp.recentOperations[0]);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 95aac60..54da643 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -33,6 +33,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Optional;
+
import javax.annotation.Nullable;
/**
@@ -43,9 +45,9 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public final class DeviceStateManagerServiceTest {
- private static final int DEFAULT_DEVICE_STATE = 0;
- private static final int OTHER_DEVICE_STATE = 1;
- private static final int UNSUPPORTED_DEVICE_STATE = 999;
+ private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
+ private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(1, "OTHER");
+ private static final DeviceState UNSUPPORTED_DEVICE_STATE = new DeviceState(999, "UNSUPPORTED");
private TestDeviceStatePolicy mPolicy;
private TestDeviceStateProvider mProvider;
@@ -61,47 +63,53 @@
@Test
public void requestStateChange() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
}
@Test
public void requestStateChange_pendingState() {
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
+ mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
mPolicy.resumeConfigure();
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
public void requestStateChange_unsupportedState() {
- mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE);
+ mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
@@ -113,85 +121,83 @@
@Test
public void requestOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE);
+ mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
// Committed state changes as there is a requested override.
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
// Committed state is set back to the requested state once the override is cleared.
mService.clearOverrideState();
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
public void requestOverrideState_unsupportedState() {
- mService.setOverrideState(UNSUPPORTED_DEVICE_STATE);
+ mService.setOverrideState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
// Committed state remains the same as the override state is unsupported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
public void supportedStatesChanged() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
// The current committed and requests states do not change because the current state remains
// supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- }
-
- @Test
- public void supportedStatesChanged_invalidState() {
- assertThrows(IllegalArgumentException.class, () -> {
- mProvider.notifySupportedDeviceStates(new int []{ INVALID_DEVICE_STATE });
- });
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
}
@Test
public void supportedStatesChanged_unsupportedRequestedState() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- mProvider.notifySupportedDeviceStates(new int []{ OTHER_DEVICE_STATE });
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE });
// The current requested state is cleared because it is no longer supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), INVALID_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState(), Optional.empty());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
}
@Test
public void supportedStatesChanged_unsupportedOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE);
+ mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
// Committed state changes as there is a requested override.
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
// Committed state is set back to the requested state as the override state is no longer
// supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
@@ -199,23 +205,27 @@
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
assertNotNull(callback.getLastNotifiedValue());
- assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE);
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
- assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
+ mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
// The callback should not have been notified of the state change as the policy is still
// pending callback.
- assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
mPolicy.resumeConfigure();
// Now that the policy is finished processing the callback should be notified of the state
// change.
- assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE);
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ OTHER_DEVICE_STATE.getIdentifier());
}
@Test
@@ -223,7 +233,8 @@
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
assertNotNull(callback.getLastNotifiedValue());
- assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
@@ -275,9 +286,8 @@
}
private static final class TestDeviceStateProvider implements DeviceStateProvider {
- private int[] mSupportedDeviceStates = new int[]{ DEFAULT_DEVICE_STATE,
+ private DeviceState[] mSupportedDeviceStates = new DeviceState[]{ DEFAULT_DEVICE_STATE,
OTHER_DEVICE_STATE };
- private int mCurrentDeviceState = DEFAULT_DEVICE_STATE;
private Listener mListener;
@Override
@@ -288,17 +298,16 @@
mListener = listener;
mListener.onSupportedDeviceStatesChanged(mSupportedDeviceStates);
- mListener.onStateChanged(mCurrentDeviceState);
+ mListener.onStateChanged(mSupportedDeviceStates[0].getIdentifier());
}
- public void notifySupportedDeviceStates(int[] supportedDeviceStates) {
+ public void notifySupportedDeviceStates(DeviceState[] supportedDeviceStates) {
mSupportedDeviceStates = supportedDeviceStates;
mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
}
- public void notifyRequestState(int state) {
- mCurrentDeviceState = state;
- mListener.onStateChanged(state);
+ public void notifyRequestState(int identifier) {
+ mListener.onStateChanged(identifier);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index ae966aa..f0b4f1b 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -227,6 +227,31 @@
}
@Test
+ public void testPhysicalStrategyRecalculateSplines() {
+ Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
+ BACKLIGHT_RANGE);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+ float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length];
+ for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) {
+ adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f;
+ }
+
+ // Default is unadjusted
+ assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
+ assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+
+ // When adjustment is turned on, adjustment array is used
+ strategy.recalculateSplines(true, adjustedNits50p);
+ assertEquals(1.3425f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
+ assertEquals(239.25f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+
+ // When adjustment is turned off, adjustment array is ignored
+ strategy.recalculateSplines(false, adjustedNits50p);
+ assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
+ assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+ }
+
+ @Test
public void testDefaultStrategyIsPhysical() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index e02a46af..d362791 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -335,6 +335,9 @@
assertEquals(0.5, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
assertEquals(3333, event.colorTemperature);
+ assertTrue(event.reduceBrightColors);
+ assertEquals(40, event.reduceBrightColorsStrength);
+ assertEquals(20f, event.reduceBrightColorsOffset, FLOAT_DELTA);
assertEquals("a.package", event.packageName);
assertEquals(0, event.userId);
assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
@@ -561,6 +564,9 @@
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
+ mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+ mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 40);
+
startTracker(mTracker);
mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
batteryChangeEvent(30, 100));
@@ -592,6 +598,9 @@
assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
assertEquals(3339, event.colorTemperature);
+ assertTrue(event.reduceBrightColors);
+ assertEquals(40, event.reduceBrightColorsStrength);
+ assertEquals(20f, event.reduceBrightColorsOffset, FLOAT_DELTA);
assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
assertTrue(event.isUserSetBrightness);
assertFalse(event.isDefaultBrightnessConfig);
@@ -606,6 +615,9 @@
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
+ mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+ mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 40);
+
startTracker(mTracker);
mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
batteryChangeEvent(30, 100));
@@ -639,6 +651,7 @@
assertEquals(brightness, event.brightness, FLOAT_DELTA);
assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
+ assertTrue(event.reduceBrightColors);
assertEquals(3339, event.colorTemperature);
}
@@ -661,6 +674,9 @@
builder.setBatteryLevel(0.7f);
builder.setNightMode(false);
builder.setColorTemperature(345);
+ builder.setReduceBrightColors(false);
+ builder.setReduceBrightColorsStrength(40);
+ builder.setReduceBrightColorsOffset(20f);
builder.setLastBrightness(50f);
builder.setColorValues(new long[] {23, 34, 45}, 1000L);
BrightnessChangeEvent event = builder.build();
@@ -684,6 +700,9 @@
assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA);
assertEquals(event.nightMode, event2.nightMode);
assertEquals(event.colorTemperature, event2.colorTemperature);
+ assertEquals(event.reduceBrightColors, event2.reduceBrightColors);
+ assertEquals(event.reduceBrightColorsStrength, event2.reduceBrightColorsStrength);
+ assertEquals(event.reduceBrightColorsOffset, event2.reduceBrightColorsOffset, FLOAT_DELTA);
assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA);
assertArrayEquals(event.colorValueBuckets, event2.colorValueBuckets);
assertEquals(event.colorSampleDuration, event2.colorSampleDuration);
@@ -1020,6 +1039,18 @@
}
@Override
+ public int getReduceBrightColorsStrength(Context context) {
+ return mSecureIntSettings.getOrDefault(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL,
+ 0);
+ }
+
+ @Override
+ public boolean isReduceBrightColorsActivated(Context context) {
+ return mSecureIntSettings.getOrDefault(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+ 0) == 1;
+ }
+
+ @Override
public DisplayedContentSample sampleColor(int noFramesToSample) {
return new DisplayedContentSample(600L,
null,
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
new file mode 100644
index 0000000..35014dc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.display.color;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ReduceBrightColorsTintControllerTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ final Resources mockResources = mock(Resources.class);
+ when(mockResources.getStringArray(
+ com.android.internal.R.array.config_reduceBrightColorsCoefficients))
+ .thenReturn(new String[]{"-0.000000000000001", "-0.955555555555554",
+ "1.000000000000000"});
+ when(mockResources.getStringArray(
+ com.android.internal.R.array.config_reduceBrightColorsCoefficientsNonlinear))
+ .thenReturn(new String[]{"-0.4429953456", "-0.2434077725", "0.9809063061"});
+ mContext = mock(Context.class);
+ when(mContext.getResources()).thenReturn(mockResources);
+ }
+
+ @Test
+ public void setAndGetMatrix() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(50);
+ tintController.setActivated(true);
+ assertThat(tintController.getStrength()).isEqualTo(50);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 0.5222222f, 0f, 0f, 0f,
+ 0f, 0.5222222f, 0f, 0f,
+ 0f, 0f, 0.5222222f, 0f,
+ 0f, 0f, 0f, 1f)
+ .inOrder();
+ }
+
+ @Test
+ public void setAndGetMatrixClampToZero() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(-50);
+ tintController.setActivated(true);
+ assertThat(tintController.getStrength()).isEqualTo(0);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 1f, 0f, 0f, 0f,
+ 0f, 1f, 0f, 0f,
+ 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f)
+ .inOrder();
+ }
+
+ @Test
+ public void setAndGetMatrixClampTo100() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(120);
+ tintController.setActivated(true);
+ assertThat(tintController.getStrength()).isEqualTo(100);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 0.04444444f, 0f, 0f, 0f,
+ 0f, 0.04444444f, 0f, 0f,
+ 0f, 0f, 0.04444444f, 0f,
+ 0f, 0f, 0f, 1f)
+ .inOrder();
+ }
+
+ @Test
+ public void returnsIdentityMatrixWhenNotActivated() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(50);
+ tintController.setActivated(true);
+ tintController.setActivated(false);
+ assertThat(tintController.getStrength()).isEqualTo(50);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 1f, 0f, 0f, 0f,
+ 0f, 1f, 0f, 0f,
+ 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f)
+ .inOrder();
+ }
+
+ @Test
+ public void getAdjustedBrightnessZeroRbcStrengthFullBrightness() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(0);
+ assertThat(tintController.getAdjustedBrightness(450f)).isEqualTo(450f);
+ }
+
+ @Test
+ public void getAdjustedBrightnessFullRbcStrengthFullBrightness() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(100);
+ assertThat(tintController.getAdjustedBrightness(450f)).isEqualTo(19.999998f);
+ }
+
+ @Test
+ public void getAdjustedBrightnessZeroRbcStrengthLowBrightness() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(0);
+ assertThat(tintController.getAdjustedBrightness(2.2f)).isEqualTo(2.2f);
+ }
+
+ @Test
+ public void getAdjustedBrightnessFullRbcStrengthLowBrightness() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(100);
+ assertThat(tintController.getAdjustedBrightness(2.2f)).isEqualTo(0.09777778f);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java
new file mode 100644
index 0000000..275e7c7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.graphics.fonts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class FontCrashDetectorTest {
+
+ private File mCacheDir;
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mCacheDir = new File(context.getCacheDir(), "UpdatableFontDirTest");
+ FileUtils.deleteContentsAndDir(mCacheDir);
+ mCacheDir.mkdirs();
+ }
+
+ @Test
+ public void detectCrash() throws Exception {
+ // Prepare a marker file.
+ File file = new File(mCacheDir, "detectCrash");
+ assertThat(file.createNewFile()).isTrue();
+
+ FontCrashDetector detector = new FontCrashDetector(file);
+ assertThat(detector.hasCrashed()).isTrue();
+
+ detector.clear();
+ assertThat(detector.hasCrashed()).isFalse();
+ assertThat(file.exists()).isFalse();
+ }
+
+ @Test
+ public void monitorCrash() {
+ File file = new File(mCacheDir, "monitorCrash");
+ FontCrashDetector detector = new FontCrashDetector(file);
+ assertThat(detector.hasCrashed()).isFalse();
+
+ FontCrashDetector.MonitoredBlock block = detector.start();
+ assertThat(file.exists()).isTrue();
+
+ block.close();
+ assertThat(file.exists()).isFalse();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index f437d1f..8331031 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -147,7 +147,8 @@
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
- assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedDate())
+ dirForPreparation.loadFontFileMap();
+ assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
.isEqualTo(expectedModifiedDate);
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
@@ -156,12 +157,13 @@
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
//
- assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedDate())
+ assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
.isNotEqualTo(expectedModifiedDate);
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(3);
assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -177,6 +179,7 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -187,6 +190,7 @@
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dirForPreparation.loadFontFileMap();
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -199,6 +203,7 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
// All font dirs (including dir for "bar.ttf") should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
@@ -211,6 +216,7 @@
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dirForPreparation.loadFontFileMap();
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -224,6 +230,7 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
// All font dirs (including dir for "bar.ttf") should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
@@ -236,6 +243,7 @@
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dirForPreparation.loadFontFileMap();
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -250,6 +258,7 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
// For foo.ttf, preinstalled font (revision 5) should be used.
assertThat(dir.getFontFileMap()).doesNotContainKey("foo.ttf");
// For bar.ttf, updated font (revision 4) should be used.
@@ -268,6 +277,7 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
new File("/dev/null"));
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -278,6 +288,7 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
installFontFile(dir, "test,1", GOOD_SIGNATURE);
assertThat(dir.getFontFileMap()).containsKey("test.ttf");
@@ -295,6 +306,7 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
installFontFile(dir, "test,1", GOOD_SIGNATURE);
Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
@@ -313,13 +325,14 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
installFontFile(dir, "test,2", GOOD_SIGNATURE);
try {
installFontFile(dir, "test,1", GOOD_SIGNATURE);
fail("Expect IllegalArgumentException");
} catch (FontManagerService.SystemFontException e) {
- assertThat(e.getErrorCode()).isEqualTo(FontManager.ERROR_CODE_DOWNGRADING);
+ assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
assertThat(dir.getFontFileMap()).containsKey("test.ttf");
assertWithMessage("Font should not be downgraded to an older revision")
@@ -333,6 +346,7 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
installFontFile(dir, "foo,1", GOOD_SIGNATURE);
installFontFile(dir, "bar,2", GOOD_SIGNATURE);
@@ -349,13 +363,14 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "test,1", "Invalid signature");
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
- .isEqualTo(FontManager.ERROR_CODE_VERIFICATION_FAILURE);
+ .isEqualTo(FontManager.RESULT_ERROR_VERIFICATION_FAILURE);
}
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -368,12 +383,13 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "test,1", GOOD_SIGNATURE);
fail("Expect IllegalArgumentException");
} catch (FontManagerService.SystemFontException e) {
- assertThat(e.getErrorCode()).isEqualTo(FontManager.ERROR_CODE_DOWNGRADING);
+ assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -398,14 +414,15 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
readonlyFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "test,2", GOOD_SIGNATURE);
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
- .isEqualTo(FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE);
+ .isEqualTo(FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG);
}
- assertThat(dir.getSystemFontConfig().getLastModifiedDate())
+ assertThat(dir.getSystemFontConfig().getLastModifiedTimeMillis())
.isEqualTo(expectedModifiedDate);
assertThat(dir.getFontFileMap()).isEmpty();
} finally {
@@ -429,13 +446,14 @@
return 0;
}
}, fakeFsverityUtil, mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "foo,1", GOOD_SIGNATURE);
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
- .isEqualTo(FontManager.ERROR_CODE_MISSING_POST_SCRIPT_NAME);
+ .isEqualTo(FontManager.RESULT_ERROR_INVALID_FONT_NAME);
}
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -456,13 +474,14 @@
return 0;
}
}, fakeFsverityUtil, mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "foo,1", GOOD_SIGNATURE);
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
- .isEqualTo(FontManager.ERROR_CODE_INVALID_FONT_FILE);
+ .isEqualTo(FontManager.RESULT_ERROR_INVALID_FONT_FILE);
}
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -491,13 +510,14 @@
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "foo,1", GOOD_SIGNATURE);
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
- .isEqualTo(FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE);
+ .isEqualTo(FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE);
}
assertThat(dir.getFontFileMap()).isEmpty();
}
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 15a9bcf..263cf48 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -21,9 +21,11 @@
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
-import android.util.Log;
+import android.annotation.NonNull;
import android.util.Pair;
+import android.util.SparseIntArray;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -58,106 +60,116 @@
* Represents running and pending jobs.
*/
class Jobs {
- public int runningFg;
- public int runningBg;
- public int pendingFg;
- public int pendingBg;
+ public final SparseIntArray running = new SparseIntArray();
+ public final SparseIntArray pending = new SparseIntArray();
public void maybeEnqueueJobs(double startRatio, double fgJobRatio) {
while (mRandom.nextDouble() < startRatio) {
if (mRandom.nextDouble() < fgJobRatio) {
- pendingFg++;
+ pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1);
} else {
- pendingBg++;
+ pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1);
}
}
}
public void maybeFinishJobs(double stopRatio) {
- for (int i = runningBg; i > 0; i--) {
+ for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
- runningBg--;
+ running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
}
}
- for (int i = runningFg; i > 0; i--) {
+ for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
- runningFg--;
+ running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
}
}
}
}
-
- private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) {
- mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig("critical",
- totalMax,
- // defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, totalMax - maxBg),
- Pair.create(WORK_TYPE_BG, minBg)),
- // defaultMax
- List.of(Pair.create(WORK_TYPE_BG, maxBg))));
+ private void recount(Jobs jobs, int totalMax,
+ @NonNull List<Pair<Integer, Integer>> minLimits,
+ @NonNull List<Pair<Integer, Integer>> maxLimits) {
+ mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig(
+ "test", totalMax, minLimits, maxLimits));
mWorkCountTracker.resetCounts();
- for (int i = 0; i < jobs.runningFg; i++) {
- mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_TOP);
- }
- for (int i = 0; i < jobs.runningBg; i++) {
- mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_BG);
- }
+ for (int i = 0; i < jobs.running.size(); ++i) {
+ final int workType = jobs.running.keyAt(i);
+ final int count = jobs.running.valueAt(i);
- for (int i = 0; i < jobs.pendingFg; i++) {
- mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_TOP);
+ for (int c = 0; c < count; ++c) {
+ mWorkCountTracker.incrementRunningJobCount(workType);
+ }
}
- for (int i = 0; i < jobs.pendingBg; i++) {
- mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_BG);
+ for (int i = 0; i < jobs.pending.size(); ++i) {
+ final int workType = jobs.pending.keyAt(i);
+ final int count = jobs.pending.valueAt(i);
+
+ for (int c = 0; c < count; ++c) {
+ mWorkCountTracker.incrementPendingJobCount(workType);
+ }
}
mWorkCountTracker.onCountDone();
+ }
- while ((jobs.pendingFg > 0
+ private void startPendingJobs(Jobs jobs) {
+ while ((jobs.pending.get(WORK_TYPE_TOP) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
- || (jobs.pendingBg > 0
+ || (jobs.pending.get(WORK_TYPE_BG) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) {
final boolean isStartingFg = mRandom.nextBoolean();
if (isStartingFg) {
- if (jobs.pendingFg > 0
+ if (jobs.pending.get(WORK_TYPE_TOP) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) {
- jobs.pendingFg--;
- jobs.runningFg++;
+ jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1);
+ jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1);
mWorkCountTracker.stageJob(WORK_TYPE_TOP);
mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
}
} else {
- if (jobs.pendingBg > 0
+ if (jobs.pending.get(WORK_TYPE_BG) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
- jobs.pendingBg--;
- jobs.runningBg++;
+ jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1);
+ jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1);
mWorkCountTracker.stageJob(WORK_TYPE_BG);
mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
}
}
}
-
- Log.i(TAG, "" + mWorkCountTracker);
}
/**
* Used by the following testRandom* tests.
*/
- private void checkRandom(Jobs jobs, int numTests, int totalMax, int maxBg, int minBg,
+ private void checkRandom(Jobs jobs, int numTests, int totalMax,
+ @NonNull List<Pair<Integer, Integer>> minLimits,
+ @NonNull List<Pair<Integer, Integer>> maxLimits,
double startRatio, double fgJobRatio, double stopRatio) {
for (int i = 0; i < numTests; i++) {
-
jobs.maybeFinishJobs(stopRatio);
jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
- startPendingJobs(jobs, totalMax, maxBg, minBg);
+ recount(jobs, totalMax, minLimits, maxLimits);
+ startPendingJobs(jobs);
- assertThat(jobs.runningFg).isAtMost(totalMax);
- assertThat(jobs.runningBg).isAtMost(totalMax);
- assertThat(jobs.runningFg + jobs.runningBg).isAtMost(totalMax);
- assertThat(jobs.runningBg).isAtMost(maxBg);
+ int totalRunning = 0;
+ for (int r = 0; r < jobs.running.size(); ++r) {
+ final int numRunning = jobs.running.valueAt(r);
+ assertWithMessage(
+ "Work type " + jobs.running.keyAt(r) + " is running too many jobs")
+ .that(numRunning).isAtMost(totalMax);
+ totalRunning += numRunning;
+ }
+ assertThat(totalRunning).isAtMost(totalMax);
+ for (Pair<Integer, Integer> maxLimit : maxLimits) {
+ assertWithMessage("Work type " + maxLimit.first + " is running too many jobs")
+ .that(jobs.running.get(maxLimit.first)).isAtMost(maxLimit.second);
+ }
}
}
@@ -170,13 +182,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.1;
final double fgJobRatio = 0.5;
final double startRatio = 0.1;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio , stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -185,13 +198,14 @@
final int numTests = 5000;
final int totalMax = 2;
- final int maxBg = 2;
- final int minBg = 0;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> minLimits = List.of();
final double stopRatio = 0.5;
final double fgJobRatio = 0.5;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -200,13 +214,14 @@
final int numTests = 5000;
final int totalMax = 2;
- final int maxBg = 2;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
final double fgJobRatio = 0.5;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -215,13 +230,14 @@
final int numTests = 5000;
final int totalMax = 10;
- final int maxBg = 2;
- final int minBg = 0;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> minLimits = List.of();
final double stopRatio = 0.5;
final double fgJobRatio = 0.5;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -230,13 +246,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
final double fgJobRatio = 0.1;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -245,13 +262,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
final double fgJobRatio = 0.9;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -260,13 +278,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.4;
final double fgJobRatio = 0.1;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -275,53 +294,213 @@
final int numTests = 5000;
final int totalMax = 6;
- final int maxBg = 4;
- final int minBg = 2;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.4;
final double fgJobRatio = 0.9;
final double startRatio = 0.5;
- checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
+ startRatio, fgJobRatio, stopRatio);
}
/** Used by the following tests */
- private void checkSimple(int totalMax, int maxBg, int minBg,
- int runningFg, int runningBg, int pendingFg, int pendingBg,
- int resultRunningFg, int resultRunningBg, int resultPendingFg, int resultPendingBg) {
+ private void checkSimple(int totalMax,
+ @NonNull List<Pair<Integer, Integer>> minLimits,
+ @NonNull List<Pair<Integer, Integer>> maxLimits,
+ @NonNull List<Pair<Integer, Integer>> running,
+ @NonNull List<Pair<Integer, Integer>> pending,
+ @NonNull List<Pair<Integer, Integer>> resultRunning,
+ @NonNull List<Pair<Integer, Integer>> resultPending) {
final Jobs jobs = new Jobs();
- jobs.runningFg = runningFg;
- jobs.runningBg = runningBg;
- jobs.pendingFg = pendingFg;
- jobs.pendingBg = pendingBg;
+ for (Pair<Integer, Integer> run : running) {
+ jobs.running.put(run.first, run.second);
+ }
+ for (Pair<Integer, Integer> pend : pending) {
+ jobs.pending.put(pend.first, pend.second);
+ }
- startPendingJobs(jobs, totalMax, maxBg, minBg);
+ recount(jobs, totalMax, minLimits, maxLimits);
+ startPendingJobs(jobs);
-// fail(mWorkerCountTracker.toString());
- assertThat(jobs.runningFg).isEqualTo(resultRunningFg);
- assertThat(jobs.runningBg).isEqualTo(resultRunningBg);
-
- assertThat(jobs.pendingFg).isEqualTo(resultPendingFg);
- assertThat(jobs.pendingBg).isEqualTo(resultPendingBg);
+ for (Pair<Integer, Integer> run : resultRunning) {
+ assertWithMessage("Incorrect running result for work type " + run.first)
+ .that(jobs.running.get(run.first)).isEqualTo(run.second);
+ }
+ for (Pair<Integer, Integer> pend : resultPending) {
+ assertWithMessage("Incorrect pending result for work type " + pend.first)
+ .that(jobs.pending.get(pend.first)).isEqualTo(pend.second);
+ }
}
-
@Test
public void testBasic() {
- // Args are:
- // First 3: Total-max, bg-max, bg-min.
- // Next 2: Running FG / BG
- // Next 2: Pending FG / BG
- // Next 4: Result running FG / BG, pending FG/BG.
- checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 1, 0, /*res run/pen=*/ 1, 0, 0, 0);
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
+ /* resPen */ List.of());
- checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 0, /*res run/pen=*/ 6, 0, 4, 0);
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 4)));
// When there are BG jobs pending, 2 (min-BG) jobs should run.
- checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0);
- checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1);
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 5)));
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 1)));
- checkSimple(8, 6, 2, /*run=*/ 0, 0, /*pen=*/ 0, 49, /*res run/pen=*/ 0, 6, 0, 43);
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(),
+ /* pen */ List.of(Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_BG, 43)));
- checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3);
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_BG, 47)));
+
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 4)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 1)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 48)));
+
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
+
+ // This could happen if we lower the effective config due to higher memory pressure after
+ // we've already started running jobs. We shouldn't stop already running jobs, but also
+ // shouldn't start new ones.
+ checkSimple(5,
+ /* min */ List.of(),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
+ }
+
+ /** Tests that the counter updates properly when jobs are stopped. */
+ @Test
+ public void testJobLifecycleLoop() {
+ final Jobs jobs = new Jobs();
+ jobs.pending.put(WORK_TYPE_TOP, 11);
+ jobs.pending.put(WORK_TYPE_BG, 10);
+
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 1));
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 5));
+
+ recount(jobs, totalMax, minLimits, maxLimits);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(6);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9);
+
+ // Stop all jobs
+ jobs.maybeFinishJobs(1);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(8);
+
+ // Stop only a bg job and make sure the counter only allows another bg job to start.
+ jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_NONE);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(7);
+
+ // Stop only a top job and make sure the counter only allows another top job to start.
+ jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_NONE);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(7);
+
+ // Now that there are no more TOP jobs pending, BG should be able to start when TOP stops.
+ for (int i = jobs.running.get(WORK_TYPE_TOP); i > 0; --i) {
+ jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+ }
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(5);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(3);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index fba36cb..c28292f 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -18,6 +18,9 @@
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+import static org.junit.Assert.fail;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.provider.DeviceConfig;
import android.util.Pair;
@@ -32,6 +35,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -58,42 +62,90 @@
}
private void check(@Nullable DeviceConfig.Properties config,
- int defaultTotal, int defaultMaxBg, int defaultMinBg,
- int expectedTotal, int expectedMaxBg, int expectedMinBg) throws Exception {
+ int defaultTotal,
+ @Nullable Pair<Integer, Integer> defaultTopLimits,
+ @Nullable Pair<Integer, Integer> defaultBgLimits,
+ boolean expectedValid, int expectedTotal,
+ @NonNull Pair<Integer, Integer> expectedTopLimits,
+ @NonNull Pair<Integer, Integer> expectedBgLimits) throws Exception {
resetConfig();
if (config != null) {
DeviceConfig.setProperties(config);
}
- final WorkTypeConfig counts = new WorkTypeConfig("test",
- defaultTotal,
- // defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, defaultTotal - defaultMaxBg),
- Pair.create(WORK_TYPE_BG, defaultMinBg)),
- // defaultMax
- List.of(Pair.create(WORK_TYPE_BG, defaultMaxBg)));
+ List<Pair<Integer, Integer>> defaultMin = new ArrayList<>();
+ List<Pair<Integer, Integer>> defaultMax = new ArrayList<>();
+ Integer val;
+ if (defaultTopLimits != null) {
+ if ((val = defaultTopLimits.first) != null) {
+ defaultMin.add(Pair.create(WORK_TYPE_TOP, val));
+ }
+ if ((val = defaultTopLimits.second) != null) {
+ defaultMax.add(Pair.create(WORK_TYPE_TOP, val));
+ }
+ }
+ if (defaultBgLimits != null) {
+ if ((val = defaultBgLimits.first) != null) {
+ defaultMin.add(Pair.create(WORK_TYPE_BG, val));
+ }
+ if ((val = defaultBgLimits.second) != null) {
+ defaultMax.add(Pair.create(WORK_TYPE_BG, val));
+ }
+ }
+
+ final WorkTypeConfig counts;
+ try {
+ counts = new WorkTypeConfig("test",
+ defaultTotal, defaultMin, defaultMax);
+ if (!expectedValid) {
+ fail("Invalid config successfully created");
+ return;
+ }
+ } catch (IllegalArgumentException e) {
+ if (expectedValid) {
+ throw e;
+ } else {
+ // Success
+ return;
+ }
+ }
counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
Assert.assertEquals(expectedTotal, counts.getMaxTotal());
- Assert.assertEquals(expectedMaxBg, counts.getMax(WORK_TYPE_BG));
- Assert.assertEquals(expectedMinBg, counts.getMinReserved(WORK_TYPE_BG));
+ Assert.assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP));
+ Assert.assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP));
+ Assert.assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG));
+ Assert.assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG));
}
@Test
public void test() throws Exception {
// Tests with various combinations.
- check(null, /*default*/ 5, 1, 0, /*expected*/ 5, 1, 0);
- check(null, /*default*/ 5, 0, 0, /*expected*/ 5, 1, 0);
- check(null, /*default*/ 0, 0, 0, /*expected*/ 1, 1, 0);
- check(null, /*default*/ -1, -1, -1, /*expected*/ 1, 1, 0);
- check(null, /*default*/ 5, 5, 5, /*expected*/ 5, 5, 4);
- check(null, /*default*/ 6, 5, 6, /*expected*/ 6, 5, 5);
- check(null, /*default*/ 4, 5, 6, /*expected*/ 4, 4, 3);
- check(null, /*default*/ 5, 1, 1, /*expected*/ 5, 1, 1);
- check(null, /*default*/ 15, 15, 15, /*expected*/ 15, 15, 14);
- check(null, /*default*/ 16, 16, 16, /*expected*/ 16, 16, 15);
- check(null, /*default*/ 20, 20, 20, /*expected*/ 16, 16, 15);
+ check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1),
+ /*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1));
+ check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0),
+ /*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1));
+ check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0),
+ /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
+ check(null, /*default*/ -1, null, Pair.create(-1, -1),
+ /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
+ check(null, /*default*/ 5, null, Pair.create(5, 5),
+ /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
+ check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5),
+ /*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5));
+ check(null, /*default*/ 4, null, Pair.create(6, 5),
+ /*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4));
+ check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1),
+ /*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1));
+ check(null, /*default*/ 15, null, Pair.create(15, 15),
+ /*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15));
+ check(null, /*default*/ 16, null, Pair.create(16, 16),
+ /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
+ check(null, /*default*/ 20, null, Pair.create(20, 20),
+ /*expected*/ false, 16, Pair.create(1, 16), Pair.create(15, 16));
+ check(null, /*default*/ 20, null, Pair.create(16, 16),
+ /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
// Test for overriding with a setting string.
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
@@ -101,15 +153,26 @@
.setInt(KEY_MAX_BG, 4)
.setInt(KEY_MIN_BG, 3)
.build(),
- /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_TOTAL, 5).build(),
- /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_BG, 4).build(),
- /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MIN_BG, 3).build(),
- /*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9));
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt(KEY_MAX_TOTAL, 20)
+ .setInt(KEY_MAX_BG, 20)
+ .setInt(KEY_MIN_BG, 8)
+ .build(),
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 161d316..6e57896 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -535,8 +535,11 @@
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
- assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
- TEST_SHORTCUT_ID)).isNotNull();
+ ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID);
+ assertThat(result).isNotNull();
+ assertThat(result.hasBirthdayToday()).isFalse();
+ assertThat(result.getStatuses()).isEmpty();
}
@Test
@@ -550,13 +553,15 @@
shortcut.setCached(ShortcutInfo.FLAG_PINNED);
mDataManager.addOrUpdateConversationInfo(shortcut);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
- TEST_SHORTCUT_ID)).isNotNull();
+ TEST_SHORTCUT_ID)).isNotNull();
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID + "1")).isNull();
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationPosted(mStatusBarNotification);
+ ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID);
@@ -568,6 +573,8 @@
result.getParentNotificationChannel().getId());
assertEquals(mStatusBarNotification.getPostTime(), result.getLastEventTimestamp());
assertTrue(result.hasActiveNotifications());
+ assertFalse(result.hasBirthdayToday());
+ assertThat(result.getStatuses()).containsExactly(cs);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index bb223b3..fb13d87 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -55,6 +55,7 @@
import java.io.File;
import java.io.InputStream;
+import java.util.Collections;
import java.util.function.Function;
/**
@@ -523,7 +524,7 @@
int flags = PackageManager.GET_META_DATA | PackageManager.GET_SIGNING_CERTIFICATES;
ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(apexFile,
- flags, false /*collectCertificates*/);
+ flags, Collections.emptyList(), false /*collectCertificates*/);
if (result.isError()) {
throw new IllegalStateException(result.getErrorMessage(), result.getException());
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
index d8910de..4dc9a90 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
@@ -19,12 +19,10 @@
import android.annotation.RawRes
import android.content.Context
import android.content.pm.parsing.ParsingPackage
-import android.content.pm.parsing.ParsingPackageImpl
import android.content.pm.parsing.ParsingPackageUtils
import android.content.pm.parsing.result.ParseInput
import android.content.pm.parsing.result.ParseInput.DeferredError
import android.content.pm.parsing.result.ParseResult
-import android.content.res.TypedArray
import android.os.Build
import androidx.test.InstrumentationRegistry
import com.android.frameworks.servicestests.R
@@ -130,7 +128,7 @@
input.copyTo(output)
}
}
- return ParsingPackageUtils.parseDefaultOneTime(file, 0 /*flags*/,
+ return ParsingPackageUtils.parseDefaultOneTime(file, 0 /*flags*/, emptyList(),
false /*collectCertificates*/)
}
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 0bea584..4f36c8a 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -40,6 +40,7 @@
import androidx.annotation.NonNull;
import com.android.server.LocalServices;
+import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
import org.junit.After;
@@ -61,7 +62,8 @@
* Run with <code>atest DeviceStateProviderImplTest</code>.
*/
public final class DeviceStateProviderImplTest {
- private final ArgumentCaptor<int[]> mIntArrayCaptor = ArgumentCaptor.forClass(int[].class);
+ private final ArgumentCaptor<DeviceState[]> mDeviceStateArrayCaptor = ArgumentCaptor.forClass(
+ DeviceState[].class);
private final ArgumentCaptor<Integer> mIntegerCaptor = ArgumentCaptor.forClass(Integer.class);
private Context mContext;
@@ -120,11 +122,12 @@
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
provider.setListener(listener);
- verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
- assertArrayEquals(new int[] { DEFAULT_DEVICE_STATE }, mIntArrayCaptor.getValue());
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ assertArrayEquals(new DeviceState[]{DEFAULT_DEVICE_STATE},
+ mDeviceStateArrayCaptor.getValue());
verify(listener).onStateChanged(mIntegerCaptor.capture());
- assertEquals(DEFAULT_DEVICE_STATE, mIntegerCaptor.getValue().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE.getIdentifier(), mIntegerCaptor.getValue().intValue());
}
@Test
@@ -146,8 +149,10 @@
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
provider.setListener(listener);
- verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
- assertArrayEquals(new int[] { 1, 2 }, mIntArrayCaptor.getValue());
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, null),
+ new DeviceState(2, null) };
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(1, mIntegerCaptor.getValue().intValue());
@@ -166,6 +171,7 @@
+ " </device-state>\n"
+ " <device-state>\n"
+ " <identifier>2</identifier>\n"
+ + " <name>CLOSED</name>\n"
+ " <conditions>\n"
+ " <lid-switch>\n"
+ " <open>false</open>\n"
@@ -180,8 +186,10 @@
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
provider.setListener(listener);
- verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
- assertArrayEquals(new int[] { 1, 2 }, mIntArrayCaptor.getValue());
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, null),
+ new DeviceState(2, "CLOSED") };
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(2, mIntegerCaptor.getValue().intValue());
@@ -189,8 +197,7 @@
Mockito.clearInvocations(listener);
provider.notifyLidSwitchChanged(0, true /* lidOpen */);
-
- verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(1, mIntegerCaptor.getValue().intValue());
}
@@ -203,6 +210,7 @@
String configString = "<device-state-config>\n"
+ " <device-state>\n"
+ " <identifier>1</identifier>\n"
+ + " <name>CLOSED</name>\n"
+ " <conditions>\n"
+ " <sensor>\n"
+ " <type>" + sensor.getStringType() + "</type>\n"
@@ -215,6 +223,7 @@
+ " </device-state>\n"
+ " <device-state>\n"
+ " <identifier>2</identifier>\n"
+ + " <name>HALF_OPENED</name>\n"
+ " <conditions>\n"
+ " <sensor>\n"
+ " <type>" + sensor.getStringType() + "</type>\n"
@@ -228,6 +237,7 @@
+ " </device-state>\n"
+ " <device-state>\n"
+ " <identifier>3</identifier>\n"
+ + " <name>OPENED</name>\n"
+ " <conditions>\n"
+ " <sensor>\n"
+ " <type>" + sensor.getStringType() + "</type>\n"
@@ -246,8 +256,10 @@
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
provider.setListener(listener);
- verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
- assertArrayEquals(new int[] { 1, 2, 3 }, mIntArrayCaptor.getValue());
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ assertArrayEquals(
+ new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"),
+ new DeviceState(3, "OPENED") }, mDeviceStateArrayCaptor.getValue());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(1, mIntegerCaptor.getValue().intValue());
@@ -256,11 +268,11 @@
SensorEvent event0 = mock(SensorEvent.class);
event0.sensor = sensor;
- FieldSetter.setField(event0, event0.getClass().getField("values"), new float[] { 180 });
+ FieldSetter.setField(event0, event0.getClass().getField("values"), new float[]{180});
provider.onSensorChanged(event0);
- verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(3, mIntegerCaptor.getValue().intValue());
@@ -268,11 +280,11 @@
SensorEvent event1 = mock(SensorEvent.class);
event1.sensor = sensor;
- FieldSetter.setField(event1, event1.getClass().getField("values"), new float[] { 90 });
+ FieldSetter.setField(event1, event1.getClass().getField("values"), new float[]{90});
provider.onSensorChanged(event1);
- verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(2, mIntegerCaptor.getValue().intValue());
@@ -280,11 +292,11 @@
SensorEvent event2 = mock(SensorEvent.class);
event2.sensor = sensor;
- FieldSetter.setField(event2, event2.getClass().getField("values"), new float[] { 0 });
+ FieldSetter.setField(event2, event2.getClass().getField("values"), new float[]{0});
provider.onSensorChanged(event2);
- verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(1, mIntegerCaptor.getValue().intValue());
}
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 1d0b595..592eea1 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -179,7 +179,8 @@
.thenReturn(mPowerSaveState);
when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
- when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true);
+ when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean()))
+ .thenReturn(true);
when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
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 c59ead9..6dcfb42 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -1123,10 +1123,10 @@
}
/**
- * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
- * like the real thing should, it also asserts preconditions.
+ * A fake implementation of {@link TimeDetectorStrategyImpl.Environment}. Besides tracking
+ * changes and behaving like the real thing should, it also asserts preconditions.
*/
- private static class FakeCallback implements TimeDetectorStrategyImpl.Callback {
+ private static class FakeEnvironment implements TimeDetectorStrategyImpl.Environment {
private boolean mAutoTimeDetectionEnabled;
private boolean mWakeLockAcquired;
private long mElapsedRealtimeMillis;
@@ -1254,41 +1254,41 @@
*/
private class Script {
- private final FakeCallback mFakeCallback;
+ private final FakeEnvironment mFakeEnvironment;
private final TimeDetectorStrategyImpl mTimeDetectorStrategy;
Script() {
- mFakeCallback = new FakeCallback();
- mTimeDetectorStrategy = new TimeDetectorStrategyImpl(mFakeCallback);
+ mFakeEnvironment = new FakeEnvironment();
+ mTimeDetectorStrategy = new TimeDetectorStrategyImpl(mFakeEnvironment);
}
Script pokeAutoTimeDetectionEnabled(boolean enabled) {
- mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
+ mFakeEnvironment.pokeAutoTimeDetectionEnabled(enabled);
return this;
}
Script pokeFakeClocks(TimestampedValue<Instant> timeInfo) {
- mFakeCallback.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis());
- mFakeCallback.pokeSystemClockMillis(timeInfo.getValue().toEpochMilli());
+ mFakeEnvironment.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis());
+ mFakeEnvironment.pokeSystemClockMillis(timeInfo.getValue().toEpochMilli());
return this;
}
Script pokeThresholds(int systemClockUpdateThreshold) {
- mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
+ mFakeEnvironment.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
return this;
}
Script pokeAutoOriginPriorities(@Origin int... autoOriginPriorities) {
- mFakeCallback.pokeAutoOriginPriorities(autoOriginPriorities);
+ mFakeEnvironment.pokeAutoOriginPriorities(autoOriginPriorities);
return this;
}
long peekElapsedRealtimeMillis() {
- return mFakeCallback.peekElapsedRealtimeMillis();
+ return mFakeEnvironment.peekElapsedRealtimeMillis();
}
long peekSystemClockMillis() {
- return mFakeCallback.peekSystemClockMillis();
+ return mFakeEnvironment.peekSystemClockMillis();
}
Script simulateTelephonyTimeSuggestion(TelephonyTimeSuggestion timeSuggestion) {
@@ -1324,13 +1324,13 @@
}
Script simulateAutoTimeDetectionToggle() {
- mFakeCallback.simulateAutoTimeZoneDetectionToggle();
+ mFakeEnvironment.simulateAutoTimeZoneDetectionToggle();
mTimeDetectorStrategy.handleAutoTimeConfigChanged();
return this;
}
Script simulateTimePassing(long clockIncrementMillis) {
- mFakeCallback.simulateTimePassing(clockIncrementMillis);
+ mFakeEnvironment.simulateTimePassing(clockIncrementMillis);
return this;
}
@@ -1342,14 +1342,14 @@
}
Script verifySystemClockWasNotSetAndResetCallTracking() {
- mFakeCallback.verifySystemClockNotSet();
- mFakeCallback.resetCallTracking();
+ mFakeEnvironment.verifySystemClockNotSet();
+ mFakeEnvironment.resetCallTracking();
return this;
}
Script verifySystemClockWasSetAndResetCallTracking(long expectedSystemClockMillis) {
- mFakeCallback.verifySystemClockWasSet(expectedSystemClockMillis);
- mFakeCallback.resetCallTracking();
+ mFakeEnvironment.verifySystemClockWasSet(expectedSystemClockMillis);
+ mFakeEnvironment.resetCallTracking();
return this;
}
@@ -1427,7 +1427,7 @@
ManualTimeSuggestion generateManualTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new ManualTimeSuggestion(utcTime);
}
@@ -1461,7 +1461,7 @@
NetworkTimeSuggestion generateNetworkTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new NetworkTimeSuggestion(utcTime);
}
@@ -1473,7 +1473,7 @@
GnssTimeSuggestion generateGnssTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new GnssTimeSuggestion(utcTime);
}
@@ -1485,7 +1485,7 @@
ExternalTimeSuggestion generateExternalTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new ExternalTimeSuggestion(utcTime);
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index a6ffd20..c8dba5f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -167,15 +167,15 @@
createConfig(null, false /* geoDetection */);
private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
- private FakeCallback mFakeCallback;
+ private FakeEnvironment mFakeEnvironment;
private MockConfigChangeListener mMockConfigChangeListener;
@Before
public void setUp() {
- mFakeCallback = new FakeCallback();
+ mFakeEnvironment = new FakeEnvironment();
mMockConfigChangeListener = new MockConfigChangeListener();
- mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(mFakeCallback);
+ mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(mFakeEnvironment);
mTimeZoneDetectorStrategy.addConfigChangeListener(mMockConfigChangeListener);
}
@@ -183,7 +183,7 @@
public void testGetCurrentUserConfiguration() {
new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
ConfigurationInternal expectedConfiguration =
- mFakeCallback.getConfigurationInternal(USER_ID);
+ mFakeEnvironment.getConfigurationInternal(USER_ID);
assertEquals(expectedConfiguration,
mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal());
}
@@ -490,7 +490,7 @@
private void makeSlotIndex1SuggestionAndCheckState(Script script, TelephonyTestCase testCase) {
// Give the next suggestion a different zone from the currently set device time zone;
- String currentZoneId = mFakeCallback.getDeviceTimeZone();
+ String currentZoneId = mFakeEnvironment.getDeviceTimeZone();
String suggestionZoneId =
"Europe/London".equals(currentZoneId) ? "Europe/Paris" : "Europe/London";
TelephonyTimeZoneSuggestion zoneSlotIndex1Suggestion =
@@ -610,9 +610,9 @@
}
/**
- * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
- * zone is actually necessary. This test proves that the strategy doesn't assume it knows the
- * current settings.
+ * The {@link TimeZoneDetectorStrategyImpl.Environment} is left to detect whether changing the
+ * time zone is actually necessary. This test proves that the strategy doesn't assume it knows
+ * the current settings.
*/
@Test
public void testTelephonySuggestionStrategyDoesNotAssumeCurrentSetting_autoTelephony() {
@@ -958,7 +958,7 @@
return builder.build();
}
- static class FakeCallback implements TimeZoneDetectorStrategyImpl.Callback {
+ static class FakeEnvironment implements TimeZoneDetectorStrategyImpl.Environment {
private final TestState<ConfigurationInternal> mConfigurationInternal = new TestState<>();
private final TestState<String> mTimeZoneId = new TestState<>();
@@ -1051,12 +1051,12 @@
private class Script {
Script initializeConfig(ConfigurationInternal configuration) {
- mFakeCallback.initializeConfig(configuration);
+ mFakeEnvironment.initializeConfig(configuration);
return this;
}
Script initializeTimeZoneSetting(String zoneId) {
- mFakeCallback.initializeTimeZoneSetting(zoneId);
+ mFakeEnvironment.initializeTimeZoneSetting(zoneId);
return this;
}
@@ -1084,7 +1084,7 @@
Script simulateManualTimeZoneSuggestion(
@UserIdInt int userId, ManualTimeZoneSuggestion manualTimeZoneSuggestion,
boolean expectedResult) {
- mFakeCallback.assertKnownUser(userId);
+ mFakeEnvironment.assertKnownUser(userId);
boolean actualResult = mTimeZoneDetectorStrategy.suggestManualTimeZone(
userId, manualTimeZoneSuggestion);
assertEquals(expectedResult, actualResult);
@@ -1104,26 +1104,26 @@
* state was last reset.
*/
Script verifyTimeZoneNotChanged() {
- mFakeCallback.assertTimeZoneNotChanged();
+ mFakeEnvironment.assertTimeZoneNotChanged();
return this;
}
/** Verifies the device's time zone has been set and clears change tracking history. */
Script verifyTimeZoneChangedAndReset(String zoneId) {
- mFakeCallback.assertTimeZoneChangedTo(zoneId);
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.assertTimeZoneChangedTo(zoneId);
+ mFakeEnvironment.commitAllChanges();
return this;
}
Script verifyTimeZoneChangedAndReset(ManualTimeZoneSuggestion suggestion) {
- mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneId());
+ mFakeEnvironment.commitAllChanges();
return this;
}
Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) {
- mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneId());
+ mFakeEnvironment.commitAllChanges();
return this;
}
@@ -1131,9 +1131,9 @@
* Verifies that the configuration has been changed to the expected value.
*/
Script verifyConfigurationChangedAndReset(ConfigurationInternal expected) {
- mFakeCallback.mConfigurationInternal.assertHasBeenSet();
- assertEquals(expected, mFakeCallback.mConfigurationInternal.getLatest());
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.mConfigurationInternal.assertHasBeenSet();
+ assertEquals(expected, mFakeEnvironment.mConfigurationInternal.getLatest());
+ mFakeEnvironment.commitAllChanges();
// Also confirm the listener triggered.
mMockConfigChangeListener.verifyOnChangeCalled();
@@ -1146,7 +1146,7 @@
* {@link TimeZoneConfiguration} have been changed.
*/
Script verifyConfigurationNotChanged() {
- mFakeCallback.mConfigurationInternal.assertHasNotBeenSet();
+ mFakeEnvironment.mConfigurationInternal.assertHasNotBeenSet();
// Also confirm the listener did not trigger.
mMockConfigChangeListener.verifyOnChangeNotCalled();
@@ -1154,7 +1154,7 @@
}
Script resetConfigurationTracking() {
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.commitAllChanges();
return this;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index 28d313b..e71c2f5 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -294,6 +294,8 @@
private InputDevice createInputDevice(int id, boolean hasVibrator) {
return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, hasVibrator, false, false, false /* hasSensor */);
+ null, hasVibrator, false, false, false /* hasSensor */, false /* hasBattery */);
+
+
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8c744c9..fc1cb70 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3536,6 +3536,28 @@
assertFalse(conversationWrapperContainsChannel(convos, channel2));
}
+ @Test
+ public void testGetConversations_parentDeleted() {
+ String convoId = "convo";
+ NotificationChannel messages =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+
+ NotificationChannel channel =
+ new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+ channel.setConversationId(messages.getId(), convoId);
+ channel.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ mHelper.permanentlyDeleteNotificationChannel(PKG_O, UID_O, "messages");
+
+ List<ConversationChannelWrapper> convos =
+ mHelper.getConversations(IntArray.wrap(new int[] {0}), true);
+
+ assertEquals(1, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, channel));
+ }
+
private boolean conversationWrapperContainsChannel(List<ConversationChannelWrapper> list,
NotificationChannel expected) {
for (ConversationChannelWrapper ccw : list) {
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 de2cc76..42b080e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1715,9 +1715,8 @@
doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
any() /* window */, any() /* attrs */,
anyInt() /* viewVisibility */, anyInt() /* displayId */,
- any() /* requestedVisibility */, any() /* outFrame */,
- any() /* outInputChannel */, any() /* outInsetsState */,
- any() /* outActiveControls */);
+ any() /* requestedVisibility */, any() /* outInputChannel */,
+ any() /* outInsetsState */, any() /* outActiveControls */);
mAtm.mWindowManager.mStartingSurfaceController
.createTaskSnapshotSurface(activity, snapshot);
} catch (RemoteException ignored) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 5c67db7..89b962b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
-import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
@@ -50,6 +50,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
@@ -408,6 +409,32 @@
}
@Test
+ public void testRestrictAppBoundsToOverrideBounds() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm);
+ final DisplayArea<DisplayArea> da = new DisplayArea<>(mWm, ANY, "Test_DA");
+ root.addChild(da, POSITION_TOP);
+ final Rect displayBounds = new Rect(0, 0, 1800, 2800);
+ final Rect displayAppBounds = new Rect(0, 100, 1800, 2800);
+ final Rect daBounds = new Rect(0, 1400, 1800, 2800);
+ root.setBounds(displayBounds);
+
+ // DA inherit parent app bounds.
+ final Configuration displayConfig = new Configuration();
+ displayConfig.windowConfiguration.setAppBounds(displayAppBounds);
+ root.onRequestedOverrideConfigurationChanged(displayConfig);
+
+ assertEquals(displayAppBounds, da.getConfiguration().windowConfiguration.getAppBounds());
+
+ // Restrict DA appBounds to override Bounds
+ da.setBounds(daBounds);
+
+ final Rect expectedDaAppBounds = new Rect(daBounds);
+ expectedDaAppBounds.intersect(displayAppBounds);
+ assertEquals(expectedDaAppBounds, da.getConfiguration().windowConfiguration.getAppBounds());
+ }
+
+ @Test
public void testGetOrientation() {
final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test");
final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
@@ -514,7 +541,7 @@
return new WindowState(mWm, mock(Session.class), new TestIWindow(), token,
null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(),
View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */,
- false /* ownerCanAddInternalSystemWindow */, false /* ownerCanUseBackgroundBlur */);
+ false /* ownerCanAddInternalSystemWindow */);
}
private WindowToken createWindowToken(int type) {
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 82ffa765..b1ea4a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -55,7 +55,6 @@
import static org.mockito.Mockito.spy;
import static org.testng.Assert.expectThrows;
-import android.app.WindowConfiguration;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -66,6 +65,7 @@
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsState;
+import android.view.RoundedCorners;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
@@ -99,6 +99,7 @@
private int mRotation = ROTATION_0;
private boolean mHasDisplayCutout;
private boolean mIsLongEdgeDisplayCutout;
+ private boolean mHasRoundedCorners;
private final Rect mDisplayBounds = new Rect();
@@ -123,6 +124,15 @@
updateDisplayFrames();
}
+ void addWindowWithRawInsetsState(WindowState win) {
+ addWindow(win);
+ // Without mPerformLayout in display content, the window cannot see any insets. Override the
+ // insets state with the global one.
+ final InsetsState insetsState =
+ win.getDisplayContent().getInsetsStateController().getRawInsetsState();
+ win.mAboveInsetsState = insetsState;
+ }
+
public void setRotation(int rotation, boolean includingWindows) {
mRotation = rotation;
updateDisplayFrames();
@@ -144,6 +154,11 @@
updateDisplayFrames();
}
+ public void addRoundedCorners() {
+ mHasRoundedCorners = true;
+ updateDisplayFrames();
+ }
+
private void updateDisplayFrames() {
mFrames = createDisplayFrames(
mDisplayContent.getInsetsStateController().getRawInsetsState());
@@ -157,9 +172,11 @@
private DisplayFrames createDisplayFrames(InsetsState insetsState) {
final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
mHasDisplayCutout, mIsLongEdgeDisplayCutout);
+ final RoundedCorners roundedCorners = mHasRoundedCorners
+ ? mDisplayContent.calculateRoundedCornersForRotation(mRotation)
+ : RoundedCorners.NO_ROUNDED_CORNERS;
return new DisplayFrames(mDisplayContent.getDisplayId(),
- insetsState,
- info.first, info.second);
+ insetsState, info.first, info.second, roundedCorners);
}
@Test
@@ -272,7 +289,7 @@
@Test
public void layoutWindowLw_fitStatusBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -283,7 +300,7 @@
@Test
public void layoutWindowLw_fitNavigationBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars());
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -294,7 +311,7 @@
@Test
public void layoutWindowLw_fitAllSides() {
mWindow.mAttrs.setFitInsetsSides(Side.all());
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -305,7 +322,7 @@
@Test
public void layoutWindowLw_fitTopOnly() {
mWindow.mAttrs.setFitInsetsSides(Side.TOP);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -315,11 +332,12 @@
@Test
public void layoutWindowLw_fitInsetsIgnoringVisibility() {
- final InsetsState state = mWindow.getInsetsState();
+ final InsetsState state =
+ mDisplayContent.getInsetsStateController().getRawInsetsState();
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitInsetsIgnoringVisibility(true);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -329,11 +347,12 @@
@Test
public void layoutWindowLw_fitInsetsNotIgnoringVisibility() {
- final InsetsState state = mWindow.getInsetsState();
+ final InsetsState state =
+ mDisplayContent.getInsetsStateController().getRawInsetsState();
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitInsetsIgnoringVisibility(false);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -349,8 +368,7 @@
state.getSource(InsetsState.ITYPE_IME).setFrame(
0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
- mWindow.mBehindIme = true;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -364,7 +382,7 @@
mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout());
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -379,7 +397,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -395,7 +413,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -411,7 +429,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -427,7 +445,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -442,7 +460,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -457,11 +475,12 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+ mDisplayContent.getInsetsStateController().getRawInsetsState()
+ .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
final InsetsState requestedState = new InsetsState();
requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
mWindow.updateRequestedVisibility(requestedState);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -476,12 +495,13 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+ mDisplayContent.getInsetsStateController().getRawInsetsState()
+ .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
final InsetsState requestedState = new InsetsState();
requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
mWindow.updateRequestedVisibility(requestedState);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -497,7 +517,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -513,7 +533,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -529,7 +549,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -545,7 +565,7 @@
mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
mWindow.mAttrs.width = DISPLAY_WIDTH;
mWindow.mAttrs.height = DISPLAY_HEIGHT;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -562,7 +582,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -576,7 +596,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -592,7 +612,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -608,7 +628,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -624,7 +644,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -638,7 +658,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
final int forwardedInsetBottom = 50;
mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
@@ -652,77 +672,13 @@
public void layoutHint_appWindow() {
mWindow.mAttrs.setFitInsetsTypes(0);
- final Rect outFrame = new Rect();
final DisplayCutout.ParcelableWrapper outDisplayCutout =
new DisplayCutout.ParcelableWrapper();
final InsetsState outState = new InsetsState();
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outFrame,
- outState, true /* localClient */);
-
- assertThat(outFrame, is(outState.getDisplayFrame()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
- is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
- assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
- is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
- }
-
- @Test
- public void layoutHint_appWindowInTask() {
- mWindow.mAttrs.setFitInsetsTypes(0);
-
- final Rect taskBounds = new Rect(100, 100, 200, 200);
- final Task task = mWindow.getTask();
- // Force the bounds because the task may resolve different bounds from Task#setBounds.
- task.getWindowConfiguration().setBounds(taskBounds);
-
- final Rect outFrame = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
- final InsetsState outState = new InsetsState();
-
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outState,
+ mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outState,
true /* localClient */);
- assertThat(outFrame, is(taskBounds));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
- is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
- assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
- is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
- }
-
- @Test
- public void layoutHint_appWindowInTask_outsideContentFrame() {
- mWindow.mAttrs.setFitInsetsTypes(0);
-
- final InsetsState state =
- mDisplayContent.getInsetsStateController().getRawInsetsState();
- final Rect contentFrame = new Rect(state.getDisplayFrame());
- contentFrame.inset(state.calculateInsets(contentFrame, Type.systemBars(),
- false /* ignoreVisibility */));
-
- // Task is in the nav bar area (usually does not happen, but this is similar enough to
- // the possible overlap with the IME)
- final Rect taskBounds = new Rect(100, contentFrame.bottom + 1,
- 200, contentFrame.bottom + 10);
-
- final Task task = mWindow.getTask();
- // Make the task floating.
- task.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
- // Force the bounds because the task may resolve different bounds from Task#setBounds.
- task.getWindowConfiguration().setBounds(taskBounds);
-
- final Rect outFrame = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
- final InsetsState outState = new InsetsState();
-
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outState,
- true /* localClient */);
-
- assertThat(outFrame, is(taskBounds));
assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
@@ -741,6 +697,8 @@
assertSimulateLayoutSameDisplayFrames();
addDisplayCutout();
assertSimulateLayoutSameDisplayFrames();
+ addRoundedCorners();
+ assertSimulateLayoutSameDisplayFrames();
}
private void assertSimulateLayoutSameDisplayFrames() {
@@ -776,9 +734,13 @@
public void testFixedRotationInsetsSourceFrame() {
doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
.rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
- final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
+ mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController()
+ .getRawInsetsState().peekSource(ITYPE_STATUS_BAR));
+ final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
+ .getSource(ITYPE_STATUS_BAR).getFrame();
mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
- final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
+ final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
+ .getSource(ITYPE_STATUS_BAR).getFrame();
assertEquals(DISPLAY_WIDTH, frame.width());
assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
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 77537a9..2163661 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -300,8 +300,9 @@
displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
+ mImeWindow.mAboveInsetsState = state;
mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
- state, displayInfo, null /* displayCutout */);
+ state, displayInfo, null /* displayCutout */, null /* roundedCorners*/);
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index e1aca55..2321a73 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -121,6 +121,8 @@
sMockWm = mock(WindowManagerService.class);
sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
sMockWm.mPolicy = mock(WindowManagerPolicy.class);
+ sMockWm.mConstants = mock(WindowManagerConstants.class);
+ sMockWm.mConstants.mRawSensorLoggingEnabled = true;
}
@AfterClass
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 70d47a5..8703c31 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -16,11 +16,13 @@
package com.android.server.wm;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.DragEvent.ACTION_DRAG_STARTED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -34,6 +36,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import android.app.PendingIntent;
@@ -278,6 +281,8 @@
@Test
public void testValidateAppShortcutArguments() {
+ doReturn(PERMISSION_GRANTED).when(mWm.mContext)
+ .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {}
@@ -329,6 +334,8 @@
@Test
public void testValidateAppTaskArguments() {
+ doReturn(PERMISSION_GRANTED).when(mWm.mContext)
+ .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index e6f24da..f91c9d0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index e0fd379..bf3ed69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -45,6 +45,7 @@
import android.app.StatusBarManager;
import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -272,7 +273,6 @@
final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar");
navBar.setHasSurface(true);
navBar.getControllableInsetProvider().setServerVisible(true);
-
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
@@ -337,11 +337,14 @@
@UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() {
- addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
- .getControllableInsetProvider().getSource().setVisible(false);
- addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
- .getControllableInsetProvider().getSource().setVisible(false);
-
+ final InsetsSource statusBarSource = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
+ .getControllableInsetProvider().getSource();
+ final InsetsSource navBarSource = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
+ .getControllableInsetProvider().getSource();
+ statusBarSource.setVisible(false);
+ navBarSource.setVisible(false);
+ mAppWindow.mAboveInsetsState.addSource(navBarSource);
+ mAppWindow.mAboveInsetsState.addSource(statusBarSource);
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
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 2766438..ee293fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -59,25 +59,6 @@
public class InsetsStateControllerTest extends WindowTestsBase {
@Test
- public void testStripForDispatch_notOwn() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
- statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
- assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
- }
-
- @Test
- public void testStripForDispatch_own() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
- .setWindow(statusBar, null, null);
- statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
- final InsetsState state = getController().getInsetsForWindow(statusBar);
- assertNull(state.peekSource(ITYPE_STATUS_BAR));
- }
-
- @Test
public void testStripForDispatch_navBar() {
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
@@ -105,6 +86,7 @@
assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_IME));
}
@Test
@@ -142,14 +124,15 @@
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
- app1.mBehindIme = true;
-
final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
- app2.mBehindIme = false;
+
+ app1.mAboveInsetsState.addSource(getController().getRawInsetsState().getSource(ITYPE_IME));
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME).isVisible());
- assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME)
+ .isVisible());
+ assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME)
+ .isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -158,7 +141,8 @@
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- app.mBehindIme = true;
+ app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
+ app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
@@ -170,10 +154,10 @@
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- app.mBehindIme = false;
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
+ .isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -210,7 +194,8 @@
// app won't get visible IME insets while above IME even when IME is visible.
assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
- assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
+ .isVisible());
// Reset invocation counter.
clearInvocations(app);
@@ -219,6 +204,8 @@
app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
mDisplayContent.computeImeTarget(true);
mDisplayContent.applySurfaceChangesTransaction();
+ app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
+ app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
// Make sure app got notified.
verify(app, atLeast(1)).notifyInsetsChanged();
@@ -234,6 +221,8 @@
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+ app.mAboveInsetsState.set(getController().getRawInsetsState());
+ child.mAboveInsetsState.set(getController().getRawInsetsState());
child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
mDisplayContent.computeImeTarget(true);
@@ -242,7 +231,8 @@
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
- assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
+ .isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -252,6 +242,7 @@
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+ app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ITYPE_IME));
child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
@@ -261,7 +252,8 @@
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
- assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
+ .isVisible());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index a045100..fa3e3ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -51,11 +52,13 @@
SurfaceControl.Transaction mTransaction;
private boolean mAreCornersRounded = false;
+ private int mColor = Color.BLACK;
@Before
public void setUp() throws Exception {
mSurfaces = new SurfaceControlMocker();
- mLetterbox = new Letterbox(mSurfaces, StubTransaction::new, () -> mAreCornersRounded);
+ mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
+ () -> mAreCornersRounded, () -> Color.valueOf(mColor));
mTransaction = spy(StubTransaction.class);
}
@@ -171,6 +174,18 @@
}
@Test
+ public void testApplySurfaceChanges_setColor() {
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 0, 0});
+
+ mColor = Color.GREEN;
+
+ assertTrue(mLetterbox.needsApplySurfaceChanges());
+ }
+
+ @Test
public void testApplySurfaceChanges_cornersNotRounded_surfaceBehindNotCreated() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
mLetterbox.applySurfaceChanges(mTransaction);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 673b00f2..21fd04e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -45,7 +45,6 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -58,7 +57,6 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
-import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.os.Bundle;
@@ -1109,28 +1107,6 @@
assertEquals(originalStackCount, mTaskContainer.getRootTaskCount());
}
- @Test
- public void testNotRecentsComponent_denyApiAccess() throws Exception {
- doReturn(PackageManager.PERMISSION_DENIED).when(mAtm)
- .checkGetTasksPermission(anyString(), anyInt(), anyInt());
- // Expect the following methods to fail due to recents component not being set
- mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
- doTestRecentTasksApis(false /* expectNoSecurityException */);
- // Don't throw for the following tests
- mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY);
- testGetTasksApis(false /* expectNoSecurityException */);
- }
-
- @Test
- public void testRecentsComponent_allowApiAccessWithoutPermissions() {
- doReturn(PackageManager.PERMISSION_DENIED).when(mAtm)
- .checkGetTasksPermission(anyString(), anyInt(), anyInt());
- // Set the recents component and ensure that the following calls do not fail
- mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT);
- doTestRecentTasksApis(true /* expectNoSecurityException */);
- testGetTasksApis(true /* expectNoSecurityException */);
- }
-
private void doTestRecentTasksApis(boolean expectCallable) {
assertSecurityException(expectCallable, () -> mAtm.removeTask(INVALID_STACK_ID));
assertSecurityException(expectCallable,
@@ -1295,13 +1271,7 @@
}
private static class TestRecentTasks extends RecentTasks {
- static final int GRANT = 0;
- static final int DENY = 1;
- static final int DENY_THROW_SECURITY_EXCEPTION = 2;
-
- private boolean mOverrideIsCallerRecents;
private boolean mIsTrimmableOverride;
- private int mIsCallerRecentsPolicy;
public boolean mLastAllowed;
@@ -1334,26 +1304,6 @@
return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID };
}
- @Override
- boolean isCallerRecents(int callingUid) {
- if (mOverrideIsCallerRecents) {
- switch (mIsCallerRecentsPolicy) {
- case GRANT:
- return true;
- case DENY:
- return false;
- case DENY_THROW_SECURITY_EXCEPTION:
- throw new SecurityException();
- }
- }
- return super.isCallerRecents(callingUid);
- }
-
- void setIsCallerRecentsOverride(int policy) {
- mOverrideIsCallerRecents = true;
- mIsCallerRecentsPolicy = policy;
- }
-
/**
* To simplify the setup for some tests, the caller can request that we only rely on the
* visible range test to determine what is trimmable. In this case, we don't try to
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 409bad4..c6be987 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -493,7 +493,7 @@
initializeRecentsAnimationController(mController, homeActivity);
// Verify RecentsAnimationController will animate visible leaf task by default.
- verify(mController).addAnimation(eq(leafTask), anyBoolean(), anyBoolean(), eq(null));
+ verify(mController).addAnimation(eq(leafTask), anyBoolean(), anyBoolean(), any());
assertTrue(leafTask.isAnimatingByRecents());
// Make sure isAnimatingByRecents will also return true when it called by the parent task.
@@ -543,6 +543,35 @@
verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
}
+ @Test
+ public void testCleanupAnimation_expectExitAnimationDone() {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityRecord homeActivity = createHomeActivity();
+ final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
+
+ initializeRecentsAnimationController(mController, homeActivity);
+ mController.startAnimation();
+
+ spyOn(win1);
+ spyOn(win1.mWinAnimator);
+ // Simulate when the window is exiting and cleanupAnimation invoked
+ // (e.g. screen off during RecentsAnimation animating), will expect the window receives
+ // onExitAnimationDone to destroy the surface when the removal is allowed.
+ win1.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
+ win1.mHasSurface = true;
+ win1.mAnimatingExit = true;
+ win1.mRemoveOnExit = true;
+ win1.mWindowRemovalAllowed = true;
+ mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ verify(win1).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), any());
+ verify(win1).onExitAnimationDone();
+ verify(win1).destroySurface(eq(false), eq(false));
+ assertFalse(win1.mAnimatingExit);
+ assertFalse(win1.mHasSurface);
+ }
+
private ActivityRecord createHomeActivity() {
final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
.setParentTask(mRootHomeTask)
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 942e1c9..6f775cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -26,6 +26,7 @@
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -38,6 +39,8 @@
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+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.assertNotEquals;
@@ -51,14 +54,12 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.TaskStackListener;
import android.app.WindowConfiguration;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager;
@@ -71,8 +72,6 @@
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-
/**
* Tests for Size Compatibility mode.
*
@@ -120,13 +119,13 @@
@Test
public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
removeGlobalMinSizeRestriction();
- // create freeform display and a freeform app
+ // Create landscape freeform display and a freeform app.
DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
.setCanRotate(false)
.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
setUpApp(display);
- // Put app window into freeform and then make it a compat app.
+ // Put app window into portrait freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -139,7 +138,7 @@
final int density = mActivity.getConfiguration().densityDpi;
- // change display configuration to fullscreen
+ // Change display configuration to fullscreen.
Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
display.onRequestedOverrideConfigurationChanged(c);
@@ -149,6 +148,8 @@
assertEquals(bounds.width(), mActivity.getBounds().width());
assertEquals(bounds.height(), mActivity.getBounds().height());
assertEquals(density, mActivity.getConfiguration().densityDpi);
+ // Size compat mode is sandboxed at the activity level.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -174,6 +175,12 @@
assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
// The decor height should be a part of the effective bounds.
assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
+ // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
+ assertActivityMaxBoundsSandboxedForLetterbox();
+ // Activity max bounds ignore notch, since an app can be shown past the notch (although app
+ // is currently limited by the notch).
+ assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
@@ -183,9 +190,17 @@
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
// The notch is no longer on top.
assertEquals(appBounds, mActivity.getBounds());
+ // Activity max bounds are sandboxed.
+ assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
+ // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
+ assertActivityMaxBoundsSandboxedForLetterbox();
+ // Activity max bounds ignore notch, since an app can be shown past the notch (although app
+ // is currently limited by the notch).
+ assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
}
@Test
@@ -213,6 +228,9 @@
assertEquals(originalBounds.width(), mActivity.getBounds().width());
assertEquals(originalBounds.height(), mActivity.getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
+ // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
+ // max aspect ratio.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
assertScaled();
}
@@ -220,11 +238,13 @@
public void testFixedScreenBoundsWhenDisplaySizeChanged() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ final DisplayContent display = mActivity.mDisplayContent;
assertFitted();
+ // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
final Rect origBounds = new Rect(mActivity.getBounds());
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
- final DisplayContent display = mActivity.mDisplayContent;
// Change the size of current display.
resizeDisplay(display, 1000, 2000);
@@ -241,6 +261,8 @@
// The position of configuration bounds should be the same as compat bounds.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
+ // Activity is sandboxed to the offset size compat bounds.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display size to a different orientation
resizeDisplay(display, 2000, 1000);
@@ -249,6 +271,8 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
+ // Activity is sandboxed to the offset size compat bounds.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// The previous resize operation doesn't consider the rotation change after size changed.
// These setups apply the requested orientation to rotation as real case that the top fixed
@@ -268,6 +292,8 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(offsetX, currentBounds.left);
assertScaled();
+ // Activity is sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -283,6 +309,8 @@
assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
+ // Activity max bounds should be sandboxed since it is letterboxed.
+ assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -294,6 +322,8 @@
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
+ // Activity max bounds should be sandboxed since it is letterboxed.
+ assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -319,14 +349,13 @@
}
@Test
- public void testMoveToDifferentOrientDisplay() {
+ public void testMoveToDifferentOrientationDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
- final int origWidth = configBounds.width();
- final int origHeight = configBounds.height();
+ final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
final int notchHeight = 100;
final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -335,37 +364,44 @@
// Move the non-resizable activity to the new display.
mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
// The configuration bounds [820, 0 - 1820, 2500] should keep the same.
- assertEquals(origWidth, configBounds.width());
- assertEquals(origHeight, configBounds.height());
+ assertEquals(originalBounds.width(), currentBounds.width());
+ assertEquals(originalBounds.height(), currentBounds.height());
assertScaled();
+ // Activity max bounds are sandboxed due to size compat mode on the new display.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
// The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
assertEquals(newDisplayBounds.height() - notchHeight,
- (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
+ (int) ((float) mActivity.getBounds().width() * originalBounds.height()
+ / originalBounds.width()));
// Recompute the natural configuration in the new display.
mActivity.clearSizeCompatMode();
mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
- assertEquals(newDisplayBounds.height(), configBounds.height());
- assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- configBounds.width());
+ assertEquals(newDisplayBounds.height(), currentBounds.height());
+ assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+ currentBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
- assertEquals(configBounds.width(), appBounds.width());
- assertEquals(configBounds.height() - notchHeight, appBounds.height());
+ assertEquals(currentBounds.width(), appBounds.width());
+ assertEquals(currentBounds.height() - notchHeight, appBounds.height());
+ // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display.
+ assertTaskMaxBoundsSandboxed();
}
@Test
- public void testFixedOrientRotateCutoutDisplay() {
+ public void testFixedOrientationRotateCutoutDisplay() {
// Create a display with a notch/cutout
final int notchHeight = 60;
- setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
+ final int width = 1000;
+ setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
.setNotch(notchHeight).build());
- // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
+ // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
+ final float maxAspect = 1.4f;
prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -373,6 +409,11 @@
final Rect origBounds = new Rect(currentBounds);
final Rect origAppBounds = new Rect(appBounds);
+ // Activity is sandboxed, and bounds include the area consumed by the notch.
+ assertActivityMaxBoundsSandboxedForLetterbox();
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
+ .isEqualTo(Math.round(width * maxAspect) + notchHeight);
+
// Although the activity is fixed orientation, force rotate the display.
rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -388,10 +429,13 @@
// The position in configuration should be global coordinates.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
+
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
- public void testFixedAspOrientChangeOrient() {
+ public void testFixedAspectRatioOrientationChangeOrientation() {
setUpDisplaySizeWithApp(1000, 2500);
final float maxAspect = 1.4f;
@@ -403,6 +447,8 @@
final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertActivityMaxBoundsSandboxedForLetterbox();
// Change the fixed orientation.
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -414,6 +460,8 @@
mActivity.getWindowConfiguration().getAppBounds().height());
assertEquals(originalAppBounds.height(),
mActivity.getWindowConfiguration().getAppBounds().width());
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -462,6 +510,8 @@
// restarted and the override configuration won't be cleared.
verify(mActivity, never()).restartProcessIfVisible();
assertScaled();
+ // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display density
display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -478,46 +528,43 @@
}
/**
- * Ensures that {@link TaskStackListener} can receive callback about the activity in size
+ * Ensures that {@link TaskOrganizerController} can receive callback about the activity in size
* compatibility mode.
*/
@Test
- public void testHandleActivitySizeCompatMode() {
+ public void testHandleActivitySizeCompatModeChanged() {
setUpDisplaySizeWithApp(1000, 2000);
+ doReturn(true).when(mTask).isOrganized();
ActivityRecord activity = mActivity;
- activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
+ activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- final ArrayList<IBinder> compatTokens = new ArrayList<>();
- mAtm.getTaskChangeNotificationController().registerTaskStackListener(
- new TaskStackListener() {
- @Override
- public void onSizeCompatModeActivityChanged(int displayId,
- IBinder activityToken) {
- compatTokens.add(activityToken);
- }
- });
-
// Resize the display so that the activity exercises size-compat mode.
resizeDisplay(mTask.mDisplayContent, 1000, 2500);
// Expect the exact token when the activity is in size compatibility mode.
- assertEquals(1, compatTokens.size());
- assertEquals(activity.appToken, compatTokens.get(0));
+ verify(mTask).onSizeCompatActivityChanged();
+ ActivityManager.RunningTaskInfo taskInfo = mTask.getTaskInfo();
- compatTokens.clear();
+ assertEquals(mActivity.appToken, taskInfo.topActivityToken);
+ assertTrue(taskInfo.topActivityInSizeCompat);
+
// Make the activity resizable again by restarting it
+ clearInvocations(mTask);
activity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
activity.mVisibleRequested = true;
activity.restartProcessIfVisible();
// The full lifecycle isn't hooked up so manually set state to resumed
- activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
+ activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
// Expect null token when switching to non-size-compat mode activity.
- assertEquals(1, compatTokens.size());
- assertEquals(null, compatTokens.get(0));
+ verify(mTask).onSizeCompatActivityChanged();
+ taskInfo = mTask.getTaskInfo();
+
+ assertEquals(mActivity.appToken, taskInfo.topActivityToken);
+ assertFalse(taskInfo.topActivityInSizeCompat);
}
@Test
@@ -539,12 +586,16 @@
// in multi-window mode.
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldUseSizeCompatMode());
+ // Activity and task should not be sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldUseSizeCompatMode());
+ // Activity and task should not be sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
}
@Test
@@ -602,12 +653,14 @@
assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
mActivity.getLetterboxInsets());
- final BarController statusBarController =
- mActivity.mDisplayContent.getDisplayPolicy().getStatusBarController();
+ final DisplayPolicy displayPolicy = mActivity.mDisplayContent.getDisplayPolicy();
// The activity doesn't fill the display, so the letterbox of the rotated activity is
// overlapped with the rotated content frame of status bar. Hence the status bar shouldn't
// be transparent.
- assertFalse(statusBarController.isTransparentAllowed(w));
+ assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+
+ // Activity is sandboxed.
+ assertActivityMaxBoundsSandboxedForLetterbox();
// Make the activity fill the display.
prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
@@ -617,7 +670,8 @@
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
- assertTrue(statusBarController.isTransparentAllowed(w));
+ assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+ assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -642,6 +696,8 @@
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(taskBounds, activityBounds);
+ // Activity inherits max bounds from task, since sandboxing applied to task.
+ assertTaskMaxBoundsSandboxed();
// Task bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), taskBounds.height());
@@ -672,6 +728,8 @@
assertScaled();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -683,29 +741,30 @@
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- Rect activityBounds = new Rect(mActivity.getBounds());
-
// App should launch in fullscreen.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertEquals(displayBounds, activityBounds);
+ // Activity and task inherit max bounds from TaskDisplayArea.
+ assertMaxBoundsInheritDisplayAreaBounds();
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- activityBounds = new Rect(mActivity.getBounds());
- assertTrue(displayBounds.width() > displayBounds.height());
+ final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
+ assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// App bounds should be 700x1400 with the ratio as the display.
- assertEquals(displayBounds.height(), activityBounds.height());
- assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
- activityBounds.width());
+ assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
+ assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
+ / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
}
@Test
@@ -738,14 +797,17 @@
final Rect displayBounds = new Rect(display.getBounds());
final Rect taskBounds = new Rect(mTask.getBounds());
final Rect newActivityBounds = new Rect(newActivity.getBounds());
+ final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width();
// Task and app bounds should be 700x1400 with the ratio as the display.
assertTrue(mTask.isTaskLetterboxed());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(taskBounds, newActivityBounds);
assertEquals(displayBounds.height(), taskBounds.height());
- assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
- taskBounds.width());
+ assertThat(taskBounds.width())
+ .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio));
+ // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
+ assertTaskMaxBoundsSandboxed();
}
@Test
@@ -785,6 +847,14 @@
assertEquals(displayBounds.height(), taskBounds.height());
assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
taskBounds.width());
+ // New activity max bounds are sandboxed due to letterbox.
+ assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(taskBounds);
+ // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width())
+ .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio));
// App bounds should be fullscreen in Task bounds.
assertFalse(newActivity.inSizeCompatMode());
@@ -813,6 +883,9 @@
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect activityBounds = new Rect(mActivity.getBounds());
mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -823,6 +896,8 @@
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
assertEquals(activityBounds, mActivity.getBounds());
+ // Activity max bounds are sandboxed due to size compat.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -838,6 +913,7 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
+ assertTaskMaxBoundsSandboxed();
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
@@ -845,6 +921,7 @@
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
+ assertActivityMaxBoundsSandboxedForSizeCompat();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
@@ -852,6 +929,7 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
+ assertTaskMaxBoundsSandboxed();
}
@Test
@@ -869,20 +947,26 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
+ // Task is letterboxed due to mismatched orientation request.
+ assertTaskMaxBoundsSandboxed();
- // Rotate display to portrait.
+ // Rotate display to landscape.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
+ // Activity max bounds are sandboxed due to unresizable app.
+ assertActivityMaxBoundsSandboxedForSizeCompat();
- // Rotate display to landscape.
+ // Rotate display to portrait.
rotateDisplay(display, ROTATION_180);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
+ // Task is letterboxed, as in first case.
+ assertTaskMaxBoundsSandboxed();
}
@Test
@@ -899,12 +983,18 @@
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(2800, displayBounds.width());
assertEquals(1400, displayBounds.height());
- taskDisplayArea.setBounds(0, 0, 2400, 1000);
+ Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
+ taskDisplayArea.setBounds(displayAreaBounds);
final Rect activityBounds = new Rect(mActivity.getBounds());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(2400, activityBounds.width());
assertEquals(1000, activityBounds.height());
+ // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(displayAreaBounds);
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(displayAreaBounds);
}
@Test
@@ -977,9 +1067,9 @@
displayPolicy.onConfigurationChanged();
final TestWindowToken token = createTestWindowToken(
- WindowManager.LayoutParams.TYPE_STATUS_BAR, displayContent);
+ TYPE_STATUS_BAR, displayContent);
final WindowManager.LayoutParams attrs =
- new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_STATUS_BAR);
+ new WindowManager.LayoutParams(TYPE_STATUS_BAR);
attrs.gravity = android.view.Gravity.TOP;
attrs.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -1052,6 +1142,48 @@
assertFalse(mActivity.hasSizeCompatBounds());
}
+ /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */
+ private void assertMaxBoundsInheritDisplayAreaBounds() {
+ final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds();
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(taskDisplayAreaBounds);
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(taskDisplayAreaBounds);
+ }
+
+ /**
+ * Asserts task-level letterboxing, so both activity and task max bounds
+ * are sandboxed to the letterbox bounds.
+ */
+ private void assertTaskMaxBoundsSandboxed() {
+ // Activity inherits max bounds from task, since sandboxing applied to task.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getBounds());
+ // Task max bounds are sandboxed due to letterbox.
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getBounds());
+ }
+
+ /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */
+ private void assertActivityMaxBoundsSandboxedForSizeCompat() {
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mActivity.getWindowConfiguration().getBounds());
+ // Task inherits max bounds from display.
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getDisplayContent().getBounds());
+ }
+
+ /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */
+ private void assertActivityMaxBoundsSandboxedForLetterbox() {
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mActivity.getBounds());
+ // Task inherits bounds from display.
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getDisplayContent().getBounds());
+ }
+
static Configuration rotateDisplay(DisplayContent display, int rotation) {
final Configuration c = new Configuration();
display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index d49956a..9372530 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -145,7 +145,7 @@
assertThat(surface).isNotNull();
verify(session).addToDisplay(any(), argThat(this::isTrustedOverlay), anyInt(), anyInt(),
- any(), any(), any(), any(), any());
+ any(), any(), any(), any());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ae85ceb..d71993d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,6 +136,11 @@
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
+ // Ensure letterbox aspect ratio is not overridden on any device target.
+ // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
+ // the below method, is set on some device form factors.
+ mService.mWindowManager.setTaskLetterboxAspectRatio(0);
+
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 53cea5a..d1d0ac6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -47,6 +47,7 @@
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsState;
+import android.view.RoundedCorners;
import android.view.Surface;
import android.view.WindowManager;
@@ -146,7 +147,7 @@
final DisplayInfo info = dc.computeScreenConfiguration(config, Surface.ROTATION_0);
final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0);
final DisplayFrames displayFrames = new DisplayFrames(dc.getDisplayId(), new InsetsState(),
- info, cutout);
+ info, cutout, RoundedCorners.NO_ROUNDED_CORNERS);
wallpaperWindowToken.applyFixedRotationTransform(info, displayFrames, config);
// Check that the wallpaper has the same frame in landscape than in portrait
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 1607f01..a1f89ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -275,7 +275,7 @@
imeSource.setFrame(imeFrame);
imeSource.setVisible(true);
w.updateRequestedVisibility(state);
- w.mBehindIme = true;
+ w.mAboveInsetsState.addSource(imeSource);
// With no insets or system decor all the frames incoming from PhoneWindowManager
// are identical.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 37983b4..77fca3d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -42,6 +42,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
@@ -55,6 +56,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
@@ -1244,6 +1246,54 @@
assertEquals(splitPrimaryRootTask, activity.getRootTask());
}
+ @Test
+ public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task rootTask = createStack();
+ final Task task = createTask(rootTask);
+ final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
+ final ArgumentCaptor<RunningTaskInfo> infoCaptor =
+ ArgumentCaptor.forClass(RunningTaskInfo.class);
+
+ assertTrue(rootTask.isOrganized());
+
+ spyOn(activity);
+ doReturn(true).when(activity).inSizeCompatMode();
+ doReturn(true).when(activity).isState(RESUMED);
+
+ // Ensure task info show top activity in size compat.
+ rootTask.onSizeCompatActivityChanged();
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ verify(organizer).onTaskInfoChanged(infoCaptor.capture());
+ RunningTaskInfo info = infoCaptor.getValue();
+ assertEquals(rootTask.mTaskId, info.taskId);
+ assertEquals(activity.appToken, info.topActivityToken);
+ assertTrue(info.topActivityInSizeCompat);
+
+ // Ensure task info show top activity that is not in foreground as not in size compat.
+ clearInvocations(organizer);
+ doReturn(false).when(activity).isState(RESUMED);
+ rootTask.onSizeCompatActivityChanged();
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ verify(organizer).onTaskInfoChanged(infoCaptor.capture());
+ info = infoCaptor.getValue();
+ assertEquals(rootTask.mTaskId, info.taskId);
+ assertEquals(activity.appToken, info.topActivityToken);
+ assertFalse(info.topActivityInSizeCompat);
+
+ // Ensure task info show non size compat top activity as not in size compat.
+ clearInvocations(organizer);
+ doReturn(true).when(activity).isState(RESUMED);
+ doReturn(false).when(activity).inSizeCompatMode();
+ rootTask.onSizeCompatActivityChanged();
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ verify(organizer).onTaskInfoChanged(infoCaptor.capture());
+ info = infoCaptor.getValue();
+ assertEquals(rootTask.mTaskId, info.taskId);
+ assertEquals(activity.appToken, info.topActivityToken);
+ assertFalse(info.topActivityInSizeCompat);
+ }
+
/**
* Verifies that task vanished is called for a specific task.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index c85991d..3492d90 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -88,8 +88,8 @@
import android.window.ITaskOrganizer;
import android.window.StartingWindowInfo;
+import com.android.internal.policy.AttributeCache;
import com.android.internal.util.ArrayUtils;
-import com.android.server.AttributeCache;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -340,7 +340,7 @@
final WindowState w = new WindowState(service, session, iWindow, token, parent,
OP_NONE, attrs, VISIBLE, ownerId, userId,
- ownerCanAddInternalSystemWindow, false /* ownerCanUseBackgroundBlur */,
+ ownerCanAddInternalSystemWindow,
powerManagerWrapper);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
// adding it to the token...
@@ -1213,8 +1213,7 @@
TestWindowState(WindowManagerService service, Session session, IWindow window,
WindowManager.LayoutParams attrs, WindowToken token) {
super(service, session, window, token, null, OP_NONE, attrs, 0, 0, 0,
- false /* ownerCanAddInternalSystemWindow */,
- false /* ownerCanUseBackgroundBlur */);
+ false /* ownerCanAddInternalSystemWindow */);
}
@Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index a4f5249..339249b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -1026,6 +1026,8 @@
writeLocked(fos, stats, version, packagesTokenData);
file.finishWrite(fos);
fos = null;
+ } catch (Exception e) {
+ // Do nothing. Exception has already been handled.
} finally {
// When fos is null (successful write), this will no-op
file.failWrite(fos);
@@ -1033,7 +1035,7 @@
}
private static void writeLocked(OutputStream out, IntervalStats stats, int version,
- PackagesTokenData packagesTokenData) throws RuntimeException {
+ PackagesTokenData packagesTokenData) throws Exception {
switch (version) {
case 1:
case 2:
@@ -1045,6 +1047,7 @@
UsageStatsProto.write(out, stats);
} catch (Exception e) {
Slog.e(TAG, "Unable to write interval stats to proto.", e);
+ throw e;
}
break;
case 5:
@@ -1053,6 +1056,7 @@
UsageStatsProtoV2.write(out, stats);
} catch (Exception e) {
Slog.e(TAG, "Unable to write interval stats to proto.", e);
+ throw e;
}
break;
default:
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 3815326..0cb1255 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -79,7 +79,6 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -111,6 +110,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
/**
* A service that collects, aggregates, and persists application usage data.
@@ -162,7 +162,7 @@
ShortcutServiceInternal mShortcutServiceInternal;
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
- private final SparseBooleanArray mUserUnlockedStates = new SparseBooleanArray();
+ private final CopyOnWriteArraySet<Integer> mUserUnlockedStates = new CopyOnWriteArraySet<>();
private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
int mUsageSource;
@@ -333,7 +333,7 @@
synchronized (mLock) {
// User was started but never unlocked so no need to report a user stopped event
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
persistPendingEventsLocked(userId);
return;
}
@@ -346,7 +346,7 @@
if (userService != null) {
userService.userStopped();
}
- mUserUnlockedStates.put(userId, false);
+ mUserUnlockedStates.remove(userId);
mUserState.put(userId, null); // release the service (mainly for GC)
}
}
@@ -360,6 +360,11 @@
UsageStatsIdleService.scheduleUpdateMappingsJob(getContext());
}
synchronized (mLock) {
+ // This should be safe to add this early. Other than reportEventOrAddToQueue, every
+ // other user grabs the lock before accessing
+ // mUserUnlockedStates. reportEventOrAddToQueue does not depend on anything other than
+ // mUserUnlockedStates, and the lock will protect the handler.
+ mUserUnlockedStates.add(userId);
// Create a user unlocked event to report
final Event unlockEvent = new Event(USER_UNLOCKED, SystemClock.elapsedRealtime());
unlockEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
@@ -377,7 +382,6 @@
initializeUserUsageStatsServiceLocked(userId, System.currentTimeMillis(),
installedPackages);
- mUserUnlockedStates.put(userId, true);
final UserUsageStatsService userService = getUserUsageStatsServiceLocked(userId);
if (userService == null) {
Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId);
@@ -780,12 +784,11 @@
}
private void reportEventOrAddToQueue(int userId, Event event) {
+ if (mUserUnlockedStates.contains(userId)) {
+ mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+ return;
+ }
synchronized (mLock) {
- if (mUserUnlockedStates.get(userId)) {
- mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
- return;
- }
-
LinkedList<Event> events = mReportedEvents.get(userId);
if (events == null) {
events = new LinkedList<>();
@@ -823,7 +826,7 @@
synchronized (mLock) {
// This should never be called directly when the user is locked
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.wtf(TAG, "Failed to report event for locked user " + userId
+ " (" + event.mPackage + "/" + event.mClass
+ " eventType:" + event.mEventType
@@ -1006,7 +1009,7 @@
final int tokenRemoved;
synchronized (mLock) {
final long timeRemoved = System.currentTimeMillis();
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
// If user is not unlocked and a package is removed for them, we will handle it
// when the user service is initialized and package manager is queried.
return;
@@ -1030,7 +1033,7 @@
*/
private boolean pruneUninstalledPackagesData(int userId) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
return false; // user is no longer unlocked
}
@@ -1050,7 +1053,7 @@
// fetch the installed packages outside the lock so it doesn't block package manager.
final HashMap<String, Long> installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM);
synchronized (mLock) {
- if (!mUserUnlockedStates.get(UserHandle.USER_SYSTEM)) {
+ if (!mUserUnlockedStates.contains(UserHandle.USER_SYSTEM)) {
return false; // user is no longer unlocked
}
@@ -1069,7 +1072,7 @@
List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime,
boolean obfuscateInstantApps) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query usage stats for locked user " + userId);
return null;
}
@@ -1103,7 +1106,7 @@
List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime,
long endTime) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query configuration stats for locked user " + userId);
return null;
}
@@ -1122,7 +1125,7 @@
List<EventStats> queryEventStats(int userId, int bucketType, long beginTime,
long endTime) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query event stats for locked user " + userId);
return null;
}
@@ -1140,7 +1143,7 @@
*/
UsageEvents queryEvents(int userId, long beginTime, long endTime, int flags) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query events for locked user " + userId);
return null;
}
@@ -1159,7 +1162,7 @@
UsageEvents queryEventsForPackage(int userId, long beginTime, long endTime,
String packageName, boolean includeTaskRoot) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query package events for locked user " + userId);
return null;
}
@@ -1203,7 +1206,7 @@
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
final int userId = mUserState.keyAt(i);
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
persistPendingEventsLocked(userId);
continue;
}
@@ -1261,7 +1264,7 @@
final int numUsers = mUserState.size();
for (int user = 0; user < numUsers; user++) {
final int userId = mUserState.keyAt(user);
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
continue;
}
ipw.println("user=" + userId);
@@ -1288,7 +1291,7 @@
final int numUsers = mUserState.size();
for (int user = 0; user < numUsers; user++) {
final int userId = mUserState.keyAt(user);
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
continue;
}
ipw.println("user=" + userId);
@@ -1344,7 +1347,7 @@
idpw.printPair("user", userId);
idpw.println();
idpw.increaseIndent();
- if (mUserUnlockedStates.get(userId)) {
+ if (mUserUnlockedStates.contains(userId)) {
if (checkin) {
mUserState.valueAt(i).checkin(idpw);
} else {
@@ -1382,7 +1385,7 @@
ipw.println("the specified user does not exist.");
return UserHandle.USER_NULL;
}
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
ipw.println("the specified user is currently in a locked state.");
return UserHandle.USER_NULL;
}
@@ -2250,12 +2253,11 @@
@Override
public byte[] getBackupPayload(int user, String key) {
+ if (!mUserUnlockedStates.contains(user)) {
+ Slog.w(TAG, "Failed to get backup payload for locked user " + user);
+ return null;
+ }
synchronized (mLock) {
- if (!mUserUnlockedStates.get(user)) {
- Slog.w(TAG, "Failed to get backup payload for locked user " + user);
- return null;
- }
-
// Check to ensure that only user 0's data is b/r for now
// Note: if backup and restore is enabled for users other than the system user, the
// #onUserUnlocked logic, specifically when the update mappings job is scheduled via
@@ -2275,7 +2277,7 @@
@Override
public void applyRestoredPayload(int user, String key, byte[] payload) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(user)) {
+ if (!mUserUnlockedStates.contains(user)) {
Slog.w(TAG, "Failed to apply restored payload for locked user " + user);
return;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index c1fb74d..be36f82 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1782,7 +1782,7 @@
}
@Override
- public void onPackageModified(String pkgName) {
+ public void onPackageModified(@NonNull String pkgName) {
// If the package modified is not in the current user, then don't bother making
// any changes as we are going to do any initialization needed when we switch users.
if (mCurUser != getChangingUserId()) {
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
index eaa3e04..5cb0c17 100644
--- a/startop/view_compiler/apk_layout_compiler.cc
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -80,7 +80,7 @@
}
namespace {
-void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets,
+void CompileApkAssetsLayouts(const std::unique_ptr<android::ApkAssets>& assets,
CompilationTarget target, std::ostream& target_out) {
android::AssetManager2 resources;
resources.SetApkAssets({assets.get()});
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index b73ef9b..be5fae4 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.net.Uri;
@@ -67,7 +68,8 @@
* Sets the participants for the resulting {@link ConnectionRequest}
* @param participants The participants to which the {@link Connection} is to connect.
*/
- public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+ public @NonNull Builder setParticipants(
+ @SuppressLint("NullableCollection") @Nullable List<Uri> participants) {
this.mParticipants = participants;
return this;
}
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 8507d85..706e3cb 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -344,6 +344,7 @@
// TODO: Instead of doing this, we should create a formal way for cloning cell identity.
// Cell identity is not an immutable object so we have to deep copy it.
mCellIdentity = CellIdentity.CREATOR.createFromParcel(p);
+ p.recycle();
}
if (nri.mVoiceSpecificInfo != null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e4386e75..65b8de2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -14242,7 +14242,7 @@
* If this policy is enabled, data will be temporarily enabled on the non-default data SIM
* during any voice calls.
*
- * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+ * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
* @hide
*/
@SystemApi
@@ -14256,7 +14256,7 @@
* will also return true for {@link ApnSetting#TYPE_MMS}.
* When disabled, the MMS APN will be governed by the same rules as all other APNs.
*
- * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+ * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
* @hide
*/
@SystemApi
@@ -14284,11 +14284,11 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void setMobileDataPolicyEnabledStatus(@MobileDataPolicy int policy, boolean enabled) {
+ public void setMobileDataPolicyEnabled(@MobileDataPolicy int policy, boolean enabled) {
try {
ITelephony service = getITelephony();
if (service != null) {
- service.setMobileDataPolicyEnabledStatus(getSubId(), policy, enabled);
+ service.setMobileDataPolicyEnabled(getSubId(), policy, enabled);
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index fb65949..6bf992e 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.telephony.ims.stub.SipDelegate;
import android.telephony.ims.stub.SipTransportImplBase;
@@ -52,7 +53,9 @@
* implementing this feature elsewhere. If all features of this {@link SipDelegate} are
* denied, this method should still be called.
*/
- void onCreated(@NonNull SipDelegate delegate, @Nullable Set<FeatureTagState> deniedTags);
+ void onCreated(@NonNull SipDelegate delegate,
+ @SuppressLint("NullableCollection") // TODO(b/154763999): Mark deniedTags @Nonnull
+ @Nullable Set<FeatureTagState> deniedTags);
/**
* This must be called by the ImsService after the framework calls
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 1faae42..a9dae89 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -138,6 +138,8 @@
* Indicates if the session is for a conference call or not. If not defined, should be
* considered {@code false}.
* Boolean extra properties - {@code true} / {@code false}.
+ *
+ * This extra is set on an instance of {@link ImsCallProfile} via {@link #setCallExtraBoolean}.
* @hide
*/
@SystemApi
@@ -174,6 +176,8 @@
* Indicates if the session can be extended to a conference call. If not defined, should be
* considered {@code false}.
* Boolean extra properties - {@code true} / {@code false}.
+ *
+ * This extra is set on an instance of {@link ImsCallProfile} via {@link #setCallExtraBoolean}.
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java
index baa0576..754814f 100644
--- a/telephony/java/android/telephony/ims/ImsUtListener.java
+++ b/telephony/java/android/telephony/ims/ImsUtListener.java
@@ -178,4 +178,11 @@
public ImsUtListener(IImsUtListener serviceInterface) {
mServiceInterface = serviceInterface;
}
+
+ /**
+ * @hide
+ */
+ public IImsUtListener getListenerInterface() {
+ return mServiceInterface;
+ }
}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index f444c62..31ba853 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -25,6 +25,7 @@
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -1325,7 +1326,7 @@
* the RCS VoLTE single registration feature. Only default messaging application may receive
* the intent.
*
- * <p>Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to specify the subscription index for which
+ * <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which
* the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
* status.
*/
@@ -1371,7 +1372,7 @@
* provisioning is done using autoconfiguration, then these parameters shall be
* sent in the HTTP get request to fetch the RCS provisioning. RCS client
* configuration must be provided by the application before registering for the
- * provisioning status events {@link #registerRcsProvisioningChangedCallback()}
+ * provisioning status events {@link #registerRcsProvisioningChangedCallback}
* @param rcc RCS client configuration {@link RcsClientConfiguration}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -1387,13 +1388,15 @@
}
/**
- * Returns a flag to indicate if the device software and the carrier
- * have the capability to support RCS Volte single IMS registration.
- * @return true if this single registration is capable, false otherwise
+ * Returns a flag to indicate whether or not the device supports IMS single registration for
+ * MMTEL and RCS features as well as if the carrier has provisioned the feature.
+ * @return true if IMS single registration is capable at this time, or false otherwise
* @throws ImsException If the remote ImsService is not available for
* any reason or the subscription associated with this instance is no
* longer active. See {@link ImsException#getCode()} for more
* information.
+ * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
+ * device supports IMS single registration.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
@@ -1430,7 +1433,7 @@
* available. This can happen if the service crashed, for example.
* It shall also throw this exception when the RCS client parameters for the
* application are not valid. In that case application must set the client
- * params (See {@link #setRcsClientConfiguration()}) and re register the
+ * params (See {@link #setRcsClientConfiguration}) and re register the
* callback.
* See {@link ImsException#getCode()} for a more detailed reason.
*/
@@ -1458,9 +1461,9 @@
* will result in a no-op.
* @param callback The existing {@link RcsProvisioningCallback} to be
* removed.
- * @see #registerRcsProvisioningChangedCallback(RcsClientConfiguration,
- * Executor, RcsProvisioningCallback) @throws IllegalArgumentException
- * if the subscription associated with this callback is invalid.
+ * @see #registerRcsProvisioningChangedCallback
+ * @throws IllegalArgumentException if the subscription associated with this callback is
+ * invalid.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterRcsProvisioningChangedCallback(
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index eddbb10..8762b6a 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -280,6 +280,12 @@
"sip_config_path_header_string";
/**
+ * The SIP User-Agent header value used by the IMS stack during IMS registration.
+ */
+ public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING =
+ "sip_config_sip_user_agent_header_string";
+
+ /**
* SIP User part string in contact header
*/
public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING =
@@ -292,12 +298,20 @@
"sip_config_p_access_network_info_header_string";
/**
- * SIP P-last-access-network-info header string
+ * The SIP P-last-access-network-info header value, populated for networks that require this
+ * information to be provided in outgoing SIP messages.
*/
public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING =
"sip_config_p_last_access_network_info_header_string";
/**
+ * The Cellular-Network-Info header value (See 3GPP 24.229, section 7.2.15), populated for
+ * networks that require this information to be provided as part of outgoing SIP messages.
+ */
+ public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING =
+ "sip_config_cellular_network_info_header_string";
+
+ /**
* SIP P-associated-uri header string
*/
public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING =
@@ -320,9 +334,11 @@
KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING,
KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING,
KEY_SIP_CONFIG_PATH_HEADER_STRING,
+ KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING,
KEY_SIP_CONFIG_URI_USER_PART_STRING,
KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING,
KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING,
+ KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING,
KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 2e9eb94..04421c9 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -24,6 +24,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.telephony.BinderCacheManager;
@@ -47,6 +48,9 @@
* This allows multiple IMS applications to forward SIP messages to/from their application for the
* purposes of providing a single IMS registration to the carrier's IMS network from potentially
* many IMS stacks implementing a subset of the supported MMTEL/RCS features.
+ * <p>
+ * This API is only supported if the device supports the
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION} feature.
* @hide
*/
@SystemApi
@@ -269,6 +273,7 @@
* {@link ImsException#getCode()} for more information.
*
* @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL
+ * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isSupported() throws ImsException {
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 06c35ea..2e35d27 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -23,6 +23,8 @@
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsEcbmListener;
+import java.util.Objects;
+
/**
* Base implementation of ImsEcbm, which implements stub versions of the methods
* in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports.
@@ -36,11 +38,27 @@
public class ImsEcbmImplBase {
private static final String TAG = "ImsEcbmImplBase";
+ private final Object mLock = new Object();
private IImsEcbmListener mListener;
- private IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
+ private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
@Override
public void setListener(IImsEcbmListener listener) {
- mListener = listener;
+ synchronized (mLock) {
+ if (mImsEcbm != null && listener != null && Objects.equals(
+ mImsEcbm.asBinder(), listener.asBinder())) {
+ return;
+ }
+ if (listener == null) {
+ mListener = null;
+ } else if (listener != null && mListener == null) {
+ mListener = listener;
+ } else {
+ // Fail fast here instead of silently overwriting the listener to another
+ // listener due to another connection connecting.
+ throw new IllegalStateException("ImsEcbmImplBase: Listener already set by "
+ + "another connection.");
+ }
+ }
}
@Override
@@ -69,9 +87,13 @@
*/
public final void enteredEcbm() {
Log.d(TAG, "Entered ECBM.");
- if (mListener != null) {
+ IImsEcbmListener listener;
+ synchronized (mLock) {
+ listener = mListener;
+ }
+ if (listener != null) {
try {
- mListener.enteredECBM();
+ listener.enteredECBM();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -85,9 +107,13 @@
*/
public final void exitedEcbm() {
Log.d(TAG, "Exited ECBM.");
- if (mListener != null) {
+ IImsEcbmListener listener;
+ synchronized (mLock) {
+ listener = mListener;
+ }
+ if (listener != null) {
try {
- mListener.exitedECBM();
+ listener.exitedECBM();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index d002903..555a47e 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -25,6 +25,7 @@
import com.android.ims.internal.IImsMultiEndpoint;
import java.util.List;
+import java.util.Objects;
/**
* Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
@@ -41,10 +42,28 @@
private static final String TAG = "MultiEndpointImplBase";
private IImsExternalCallStateListener mListener;
- private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+ private final Object mLock = new Object();
+ private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+
@Override
public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
- mListener = listener;
+ synchronized (mLock) {
+ if (mListener != null && listener != null && Objects.equals(
+ mListener.asBinder(), listener.asBinder())) {
+ return;
+ }
+
+ if (listener == null) {
+ mListener = null;
+ } else if (listener != null && mListener == null) {
+ mListener = listener;
+ } else {
+ // Fail fast here instead of silently overwriting the listener to another
+ // listener due to another connection connecting.
+ throw new IllegalStateException("ImsMultiEndpointImplBase: Listener already"
+ + " set by another connection.");
+ }
+ }
}
@Override
@@ -65,9 +84,13 @@
*/
public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) {
Log.d(TAG, "ims external call state update triggered.");
- if (mListener != null) {
+ IImsExternalCallStateListener listener;
+ synchronized (mLock) {
+ listener = mListener;
+ }
+ if (listener != null) {
try {
- mListener.onImsExternalCallStateUpdate(externalCallDialogs);
+ listener.onImsExternalCallStateUpdate(externalCallDialogs);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index f5219d5..eef4fca 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -29,6 +29,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Base implementation of IMS UT interface, which implements stubs. Override these methods to
@@ -116,7 +117,10 @@
*/
public static final int INVALID_RESULT = -1;
- private IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+ private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+ private final Object mLock = new Object();
+ private ImsUtListener mUtListener;
+
@Override
public void close() throws RemoteException {
ImsUtImplBase.this.close();
@@ -202,7 +206,26 @@
@Override
public void setListener(IImsUtListener listener) throws RemoteException {
- ImsUtImplBase.this.setListener(new ImsUtListener(listener));
+ synchronized (mLock) {
+ if (mUtListener != null && listener != null && Objects.equals(
+ mUtListener.getListenerInterface().asBinder(), listener.asBinder())) {
+ return;
+ }
+
+ if (listener == null) {
+ mUtListener = null;
+ } else if (listener != null && mUtListener == null) {
+ mUtListener = new ImsUtListener(listener);
+ } else {
+ // This is a limitation of the current API surface, there can only be one
+ // listener connected. Fail fast instead of silently overwriting the other
+ // listener.
+ throw new IllegalStateException("ImsUtImplBase#setListener: listener already "
+ + "set by another connected interface!");
+ }
+ }
+
+ ImsUtImplBase.this.setListener(mUtListener);
}
@Override
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ee6c36c..0cd17da3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2172,7 +2172,7 @@
*/
String getMmsUAProfUrl(int subId);
- void setMobileDataPolicyEnabledStatus(int subscriptionId, int policy, boolean enabled);
+ void setMobileDataPolicyEnabled(int subscriptionId, int policy, boolean enabled);
boolean isMobileDataPolicyEnabled(int subscriptionId, int policy);
@@ -2356,6 +2356,21 @@
int removeContactFromEab(int subId, String contacts);
/**
+ * Get the EAB contact from the EAB database.
+ */
+ String getContactFromEab(String contact);
+
+ /*
+ * Check whether the device supports RCS User Capability Exchange or not.
+ */
+ boolean getDeviceUceEnabled();
+
+ /*
+ * Set the device supports RCS User Capability Exchange.
+ */
+ void setDeviceUceEnabled(boolean isEnabled);
+
+ /**
* Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
* specified thresholds.
*/
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index 02c75ed..39dc9c2 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -16,6 +16,7 @@
name: "ApkVerityTest",
srcs: ["src/**/*.java"],
libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
+ static_libs: ["frameworks-base-hostutils"],
test_suites: ["general-tests", "vts"],
target_required: [
"block_device_writer_module",
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index 629b6c7..d0eb9be 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -21,10 +21,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
import android.platform.test.annotations.RootPermissionTest;
+import com.android.fsverity.AddFsVerityCertRule;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
@@ -35,6 +35,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -85,40 +86,25 @@
private static final String DAMAGING_EXECUTABLE = "/data/local/tmp/block_device_writer";
private static final String CERT_PATH = "/data/local/tmp/ApkVerityTestCert.der";
- private static final String APK_VERITY_STANDARD_MODE = "2";
-
/** Only 4K page is supported by fs-verity currently. */
private static final int FSVERITY_PAGE_SIZE = 4096;
+ @Rule
+ public final AddFsVerityCertRule mAddFsVerityCertRule =
+ new AddFsVerityCertRule(this, CERT_PATH);
+
private ITestDevice mDevice;
- private String mKeyId;
@Before
public void setUp() throws DeviceNotAvailableException {
mDevice = getDevice();
- String apkVerityMode = mDevice.getProperty("ro.apk_verity.mode");
- assumeTrue(mDevice.getLaunchApiLevel() >= 30
- || APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
-
- mKeyId = expectRemoteCommandToSucceed(
- "mini-keyctl padd asymmetric fsv_test .fs-verity < " + CERT_PATH).trim();
- if (!mKeyId.matches("^\\d+$")) {
- String keyId = mKeyId;
- mKeyId = null;
- fail("Key ID is not decimal: " + keyId);
- }
-
uninstallPackage(TARGET_PACKAGE);
}
@After
public void tearDown() throws DeviceNotAvailableException {
uninstallPackage(TARGET_PACKAGE);
-
- if (mKeyId != null) {
- expectRemoteCommandToSucceed("mini-keyctl unlink " + mKeyId + " .fs-verity");
- }
}
@Test
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index c945aea..0792e8b 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -18,28 +18,7 @@
name: "FlickerTests",
srcs: ["src/**/*.java", "src/**/*.kt"],
manifest: "AndroidManifest.xml",
- test_config: "AndroidTestPhysicalDevices.xml",
- platform_apis: true,
- certificate: "platform",
- test_suites: ["device-tests"],
- libs: ["android.test.runner"],
- static_libs: [
- "androidx.test.ext.junit",
- "flickertestapplib",
- "flickerlib",
- "truth-prebuilt",
- "launcher-helper-lib",
- "launcher-aosp-tapl",
- "platform-test-annotations",
- ],
-}
-
-
-android_test {
- name: "FlickerTestsVirtual",
- srcs: ["src/**/*.java", "src/**/*.kt"],
- manifest: "AndroidManifest.xml",
- test_config: "AndroidTestVirtualDevices.xml",
+ test_config: "AndroidTest.xml",
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
diff --git a/tests/FlickerTests/AndroidTestPhysicalDevices.xml b/tests/FlickerTests/AndroidTest.xml
similarity index 95%
rename from tests/FlickerTests/AndroidTestPhysicalDevices.xml
rename to tests/FlickerTests/AndroidTest.xml
index b1cee5c..e68fbd8 100644
--- a/tests/FlickerTests/AndroidTestPhysicalDevices.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -25,7 +25,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.wm.flicker"/>
- <option name="include-annotation" value="androidx.test.filters.RequiresDevice" />
<option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6600s" />
diff --git a/tests/FlickerTests/AndroidTestVirtualDevices.xml b/tests/FlickerTests/AndroidTestVirtualDevices.xml
deleted file mode 100644
index 9a5413a..0000000
--- a/tests/FlickerTests/AndroidTestVirtualDevices.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright 2018 Google Inc. All Rights Reserved.
- -->
-<configuration description="Runs WindowManager Flicker Tests">
- <option name="test-tag" value="FlickerTests" />
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on" />
- <!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true" />
- <!-- set WM tracing verbose level to all -->
- <option name="run-command" value="cmd window tracing level all" />
- <!-- inform WM to log all transactions -->
- <option name="run-command" value="cmd window tracing transaction" />
- <!-- restart launcher to activate TAPL -->
- <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
- <!-- reboot the device to teardown any crashed tests -->
- <option name="cleanup-action" value="REBOOT" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="FlickerTests.apk"/>
- <option name="test-file-name" value="FlickerTestApp.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.server.wm.flicker"/>
- <option name="exclude-annotation" value="androidx.test.filters.RequiresDevice" />
- <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
- <option name="shell-timeout" value="6600s" />
- <option name="test-timeout" value="6000s" />
- <option name="hidden-api-checks" value="false" />
- </test>
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/sdcard/flicker" />
- <option name="collect-on-run-ended-only" value="true" />
- <option name="clean-up" value="true" />
- </metrics_collector>
-</configuration>
diff --git a/tests/FlickerTests/TEST_MAPPING b/tests/FlickerTests/TEST_MAPPING
deleted file mode 100644
index db251b9..0000000
--- a/tests/FlickerTests/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "postsubmit": [
- // Run tests on real device
- {
- "name": "FlickerTests",
- "keywords": ["primary-device"]
- },
- // Also run the tests in the cloud
- {
- "name": "FlickerTests"
- }
- ]
-}
\ No newline at end of file
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 ba12fbe..89c6663 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,37 +17,40 @@
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.LayersAssertionBuilder
-import com.android.server.wm.flicker.dsl.WmAssertionBuilder
+import com.android.server.wm.flicker.dsl.EventLogAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
+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 NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
-const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
+const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
const val WALLPAPER_TITLE = "Wallpaper"
@JvmOverloads
-fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(
+fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
+ this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
}
}
@JvmOverloads
-fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(
+fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
all("navBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
+ this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
}
}
-fun WmAssertionBuilder.visibleWindowsShownMoreThanOneConsecutiveEntry(
+fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
ignoreWindows: List<String> = emptyList(),
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -57,7 +60,7 @@
}
}
-fun WmAssertionBuilder.launcherReplacesAppWindowAsTopWindow(
+fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -69,7 +72,7 @@
}
}
-fun WmAssertionBuilder.wallpaperWindowBecomesVisible(
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -80,7 +83,7 @@
}
}
-fun WmAssertionBuilder.wallpaperWindowBecomesInvisible(
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -91,7 +94,7 @@
}
}
-fun WmAssertionBuilder.appWindowAlwaysVisibleOnTop(
+fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
packageName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -101,7 +104,7 @@
}
}
-fun WmAssertionBuilder.appWindowBecomesVisible(
+fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
appName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -113,8 +116,20 @@
}
}
+fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
+ appName: String,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("appWindowBecomesInVisible", bugId, enabled) {
+ this.showsAppWindow(appName)
+ .then()
+ .hidesAppWindow(appName)
+ }
+}
+
@JvmOverloads
-fun LayersAssertionBuilder.noUncoveredRegions(
+fun LayersAssertionBuilderLegacy.noUncoveredRegions(
beginRotation: Int,
endRotation: Int = beginRotation,
allStates: Boolean = true,
@@ -144,49 +159,49 @@
}
@JvmOverloads
-fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible(
+fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible(
rotatesScreen: Boolean = false,
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
if (rotatesScreen) {
all("navBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ this.showsLayer(NAV_BAR_LAYER_NAME)
.then()
- .hidesLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ .hidesLayer(NAV_BAR_LAYER_NAME)
.then()
- .showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ .showsLayer(NAV_BAR_LAYER_NAME)
}
} else {
all("navBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ this.showsLayer(NAV_BAR_LAYER_NAME)
}
}
}
@JvmOverloads
-fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible(
+fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible(
rotatesScreen: Boolean = false,
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
if (rotatesScreen) {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(STATUS_BAR_WINDOW_TITLE)
+ this.showsLayer(STATUS_BAR_LAYER_NAME)
.then()
- hidesLayer(STATUS_BAR_WINDOW_TITLE)
+ hidesLayer(STATUS_BAR_LAYER_NAME)
.then()
- .showsLayer(STATUS_BAR_WINDOW_TITLE)
+ .showsLayer(STATUS_BAR_LAYER_NAME)
}
} else {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(STATUS_BAR_WINDOW_TITLE)
+ this.showsLayer(STATUS_BAR_LAYER_NAME)
}
}
}
@JvmOverloads
-fun LayersAssertionBuilder.navBarLayerRotatesAndScales(
+fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales(
beginRotation: Int,
endRotation: Int = beginRotation,
bugId: Int = 0,
@@ -196,21 +211,21 @@
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) {
- this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
}
end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) {
- this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos)
}
if (startingPos == endingPos) {
all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
- this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
}
}
}
@JvmOverloads
-fun LayersAssertionBuilder.statusBarLayerRotatesScales(
+fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales(
beginRotation: Int,
endRotation: Int = beginRotation,
bugId: Int = 0,
@@ -220,14 +235,14 @@
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) {
- this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
+ this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, startingPos)
}
end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) {
- this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos)
+ this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, endingPos)
}
}
-fun LayersAssertionBuilder.visibleLayersShownMoreThanOneConsecutiveEntry(
+fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry(
ignoreLayers: List<String> = emptyList(),
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -237,7 +252,7 @@
}
}
-fun LayersAssertionBuilder.appLayerReplacesWallpaperLayer(
+fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer(
appName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -249,7 +264,7 @@
}
}
-fun LayersAssertionBuilder.wallpaperLayerReplacesAppLayer(
+fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -261,7 +276,7 @@
}
}
-fun LayersAssertionBuilder.layerAlwaysVisible(
+fun LayersAssertionBuilderLegacy.layerAlwaysVisible(
packageName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -271,7 +286,7 @@
}
}
-fun LayersAssertionBuilder.layerBecomesVisible(
+fun LayersAssertionBuilderLegacy.layerBecomesVisible(
packageName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -283,7 +298,7 @@
}
}
-fun LayersAssertionBuilder.layerBecomesInvisible(
+fun LayersAssertionBuilderLegacy.layerBecomesInvisible(
packageName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -295,7 +310,7 @@
}
}
-fun EventLogAssertionBuilder.focusChanges(
+fun EventLogAssertionBuilderLegacy.focusChanges(
vararg windows: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -305,7 +320,7 @@
}
}
-fun EventLogAssertionBuilder.focusDoesNotChange(
+fun EventLogAssertionBuilderLegacy.focusDoesNotChange(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
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 1d69fe4..b5fd4a5 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
@@ -20,7 +20,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -56,18 +55,16 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class CloseAppBackButtonTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation, repetitions = 5)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation, repetitions = 5) { configuration ->
withTestName { buildTestTag("closeAppBackButton", configuration) }
repeat { configuration.repetitions }
setup {
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 f1d08c2..584e4b1 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
@@ -20,7 +20,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -55,18 +54,16 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class CloseAppHomeButtonTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation, repetitions = 5)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation, repetitions = 5) { configuration ->
withTestName { buildTestTag("closeAppHomeButton", configuration) }
repeat { configuration.repetitions }
setup {
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 b569eda..1a47449 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
@@ -41,6 +41,9 @@
wmHelper.waitForRotation(rotation)
wmHelper.waitForNavBarStatusBarVisible()
wmHelper.waitForAppTransitionIdle()
+
+ // Ensure WindowManagerService wait until all animations have completed
+ instrumentation.getUiAutomation().syncInputTransactions()
} catch (e: RemoteException) {
throw RuntimeException(e)
}
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 d3eefd0..fde97ba 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
@@ -21,7 +21,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
@@ -55,18 +54,16 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 178015460)
class CloseImeAutoOpenWindowToAppTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
- return FlickerTestRunnerFactory(instrumentation, repetitions = 5)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation, repetitions = 5) { configuration ->
val testApp = ImeAppAutoFocusHelper(instrumentation,
configuration.startRotation)
withTestName { buildTestTag("imeToAppAutoOpen", configuration) }
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 f5bb8e1..ab7c08f 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
@@ -20,7 +20,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
@@ -53,18 +52,16 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class CloseImeAutoOpenWindowToHomeTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
- return FlickerTestRunnerFactory(instrumentation, repetitions = 5)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation, repetitions = 5) { configuration ->
val testApp = ImeAppAutoFocusHelper(instrumentation,
configuration.startRotation)
withTestName {
@@ -114,7 +111,8 @@
enabled = !configuration.startRotation.isRotated())
statusBarLayerIsAlwaysVisible(
enabled = !configuration.startRotation.isRotated())
- visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
+ enabled = !configuration.startRotation.isRotated())
imeLayerBecomesInvisible()
imeAppLayerBecomesInvisible(testApp)
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 a363192..0503cce0 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
@@ -21,7 +21,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -54,10 +53,8 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 178015460)
class CloseImeWindowToAppTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@@ -65,8 +62,8 @@
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = ImeAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation, repetitions = 5)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation, repetitions = 5) { configuration ->
withTestName { buildTestTag("imeToApp", configuration) }
repeat { configuration.repetitions }
setup {
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 f474ec2..9cb075b 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
@@ -21,7 +21,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -54,18 +53,16 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 178015460)
class CloseImeWindowToHomeTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = ImeAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation, repetitions = 5)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation, repetitions = 5) { configuration ->
withTestName { buildTestTag("imeToHome", configuration) }
repeat { configuration.repetitions }
setup {
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 96c2009..c775cb8 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,13 +17,13 @@
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.dsl.LayersAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
const val IME_WINDOW_TITLE = "InputMethod"
@JvmOverloads
-fun LayersAssertionBuilder.imeLayerBecomesVisible(
+fun LayersAssertionBuilderLegacy.imeLayerBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -34,7 +34,7 @@
}
}
-fun LayersAssertionBuilder.imeLayerBecomesInvisible(
+fun LayersAssertionBuilderLegacy.imeLayerBecomesInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -45,7 +45,7 @@
}
}
-fun LayersAssertionBuilder.imeAppLayerIsAlwaysVisible(
+fun LayersAssertionBuilderLegacy.imeAppLayerIsAlwaysVisible(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -55,7 +55,7 @@
}
}
-fun WmAssertionBuilder.imeAppWindowIsAlwaysVisible(
+fun WmAssertionBuilderLegacy.imeAppWindowIsAlwaysVisible(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -65,7 +65,7 @@
}
}
-fun WmAssertionBuilder.imeWindowBecomesVisible(
+fun WmAssertionBuilderLegacy.imeWindowBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -76,7 +76,7 @@
}
}
-fun WmAssertionBuilder.imeWindowBecomesInvisible(
+fun WmAssertionBuilderLegacy.imeWindowBecomesInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -87,7 +87,7 @@
}
}
-fun WmAssertionBuilder.imeAppWindowBecomesVisible(
+fun WmAssertionBuilderLegacy.imeAppWindowBecomesVisible(
windowName: String,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -99,7 +99,7 @@
}
}
-fun WmAssertionBuilder.imeAppWindowBecomesInvisible(
+fun WmAssertionBuilderLegacy.imeAppWindowBecomesInvisible(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -111,7 +111,7 @@
}
}
-fun LayersAssertionBuilder.imeAppLayerBecomesInvisible(
+fun LayersAssertionBuilderLegacy.imeAppLayerBecomesInvisible(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
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 ff07398..13c6cd7 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
@@ -21,7 +21,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -57,18 +56,16 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 178015460)
class OpenImeWindowTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = ImeAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation, repetitions = 5)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation, repetitions = 5) { configuration ->
withTestName { buildTestTag("openIme", configuration) }
repeat { configuration.repetitions }
setup {
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 d2d5960..6cc64df 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
@@ -20,7 +20,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
@@ -58,18 +57,16 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ReOpenImeWindowTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
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(instrumentation, repetitions = 1)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation, repetitions = 1) { configuration ->
val testApp = ImeAppAutoFocusHelper(instrumentation,
configuration.startRotation)
withTestName { buildTestTag("reOpenImeAutoFocus", configuration) }
@@ -90,7 +87,6 @@
transitions {
device.reopenAppFromOverview()
wmHelper.waitImeWindowShown()
- // wmHelper.waitForFullScreenApp(testAppComponentName)
}
teardown {
test {
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 ba2ee5f..1bd1190 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,9 +17,9 @@
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.dsl.WmAssertionBuilderLegacy
-fun WmAssertionBuilder.appWindowReplacesLauncherAsTopWindow(
+fun WmAssertionBuilderLegacy.appWindowReplacesLauncherAsTopWindow(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
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 cf25987..010ebf2 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
@@ -20,7 +20,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.endRotation
@@ -57,18 +56,16 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class OpenAppColdTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation) { configuration ->
withTestName { buildTestTag("openAppCold", testApp, configuration) }
repeat { configuration.repetitions }
setup {
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 c957b3f..5e08921 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
@@ -20,7 +20,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.endRotation
@@ -58,18 +57,16 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class OpenAppFromOverviewTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation, repetitions = 5)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation, repetitions = 5) { configuration ->
withTestName { buildTestTag("openAppFromOverview", configuration) }
repeat { configuration.repetitions }
setup {
@@ -118,7 +115,7 @@
navBarLayerIsAlwaysVisible(
enabled = Surface.ROTATION_0 == configuration.endRotation)
visibleLayersShownMoreThanOneConsecutiveEntry(
- enabled = Surface.ROTATION_0 == configuration.endRotation)
+ enabled = false)
appLayerReplacesWallpaperLayer(testApp.`package`)
}
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 fd99be2..092cd4d 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
@@ -20,7 +20,6 @@
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.endRotation
@@ -56,19 +55,15 @@
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppWarmTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+class OpenAppWarmTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = SimpleAppHelper(instrumentation)
- return FlickerTestRunnerFactory(instrumentation)
- .buildTest { configuration ->
+ return FlickerTestRunnerFactory.getInstance()
+ .buildTest(instrumentation) { configuration ->
withTestName { buildTestTag("openAppWarm", testApp, configuration) }
repeat { configuration.repetitions }
setup {
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 fe3ab04..1c44b21 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
@@ -20,7 +20,6 @@
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -54,10 +53,8 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ChangeAppRotationTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object : RotationTransition(InstrumentationRegistry.getInstrumentation()) {
override val testApp: StandardAppHelper
get() = SimpleAppHelper(instrumentation)
@@ -66,7 +63,7 @@
private const val SCREENSHOT_LAYER = "RotationLayer"
- @Parameterized.Parameters(name = "{0}")
+ @Parameterized.Parameters(name = "{0}1}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
@@ -119,8 +116,8 @@
}
}
- return FlickerTestRunnerFactory(instrumentation, repetitions = 5)
- .buildRotationTest(transition, testSpec)
+ return FlickerTestRunnerFactory.getInstance()
+ .buildRotationTest(instrumentation, transition, testSpec, repetitions = 5)
}
}
}
\ 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 e25c734..f04131b 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
@@ -20,7 +20,6 @@
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.endRotation
@@ -32,7 +31,6 @@
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.helpers.isRotated
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -58,10 +56,8 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class SeamlessAppRotationTest(
- testName: String,
- flickerProvider: () -> Flicker,
- cleanUp: Boolean
-) : FlickerTestRunner(testName, flickerProvider, cleanUp) {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
companion object : RotationTransition(InstrumentationRegistry.getInstrumentation()) {
override val testApp: StandardAppHelper
get() = SeamlessRotationAppHelper(instrumentation)
@@ -70,6 +66,8 @@
ActivityOptions.EXTRA_STARVE_UI_THREAD to configuration.starveUiThread.toString()
)
+ private val testFactory = FlickerTestRunnerFactory.getInstance()
+
private val Bundle.starveUiThread
get() = this.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, false)
@@ -80,8 +78,8 @@
}
@JvmStatic
- private fun FlickerTestRunnerFactory.getConfigurations(): List<Bundle> {
- return this.getConfigRotationTests().flatMap {
+ private fun getConfigurations(): List<Bundle> {
+ return testFactory.getConfigRotationTests().flatMap {
val defaultRun = it.createConfig(starveUiThread = false)
val busyUiRun = it.createConfig(starveUiThread = true)
listOf(defaultRun, busyUiRun)
@@ -91,8 +89,7 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
- val factory = FlickerTestRunnerFactory(instrumentation, repetitions = 2)
- val configurations = factory.getConfigurations()
+ val configurations = getConfigurations()
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
val extra = if (configuration.starveUiThread) {
@@ -161,7 +158,8 @@
}
}
- return factory.buildRotationTest(transition, testSpec, configurations)
+ return testFactory.buildRotationTest(instrumentation, transition, testSpec,
+ deviceConfigurations = configurations, repetitions = 2)
}
}
}
\ No newline at end of file
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 104758d..5381009 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -98,6 +98,8 @@
private final TestClock mTestClock = new TestClock();
private TestLooper mTestLooper;
private Context mSpyContext;
+ // Keep track of all created watchdogs to apply device config changes
+ private List<PackageWatchdog> mAllocatedWatchdogs;
@Mock
private ConnectivityModuleConnector mConnectivityModuleConnector;
@Mock
@@ -112,7 +114,8 @@
MockitoAnnotations.initMocks(this);
new File(InstrumentationRegistry.getContext().getFilesDir(),
"package-watchdog.xml").delete();
- adoptShellPermissions(Manifest.permission.READ_DEVICE_CONFIG);
+ adoptShellPermissions(Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.WRITE_DEVICE_CONFIG);
mTestLooper = new TestLooper();
mSpyContext = spy(InstrumentationRegistry.getContext());
when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
@@ -157,12 +160,23 @@
return storedValue == null ? defaultValue : Long.parseLong(storedValue);
}
).when(() -> SystemProperties.getLong(anyString(), anyLong()));
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED,
+ Boolean.toString(true), false);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+ Integer.toString(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT), false);
+
+ mAllocatedWatchdogs = new ArrayList<>();
}
@After
public void tearDown() throws Exception {
dropShellPermissions();
mSession.finishMocking();
+ mAllocatedWatchdogs.clear();
}
@Test
@@ -611,10 +625,6 @@
*/
@Test
public void testExplicitHealthCheckStateChanges() throws Exception {
- adoptShellPermissions(
- Manifest.permission.WRITE_DEVICE_CONFIG,
- Manifest.permission.READ_DEVICE_CONFIG);
-
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
@@ -807,9 +817,6 @@
/** Test default values are used when device property is invalid. */
@Test
public void testInvalidConfig_watchdogTriggerFailureCount() {
- adoptShellPermissions(
- Manifest.permission.WRITE_DEVICE_CONFIG,
- Manifest.permission.READ_DEVICE_CONFIG);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
Integer.toString(-1), /*makeDefault*/false);
@@ -835,9 +842,6 @@
/** Test default values are used when device property is invalid. */
@Test
public void testInvalidConfig_watchdogTriggerDurationMillis() {
- adoptShellPermissions(
- Manifest.permission.WRITE_DEVICE_CONFIG,
- Manifest.permission.READ_DEVICE_CONFIG);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
Integer.toString(2), /*makeDefault*/false);
@@ -850,7 +854,6 @@
watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE);
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
- mTestLooper.dispatchAll();
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1);
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -862,7 +865,6 @@
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
- mTestLooper.dispatchAll();
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS - 1);
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -917,9 +919,6 @@
/** Test we are notified when enough failures are triggered within any window. */
@Test
public void testFailureTriggerWindow() {
- adoptShellPermissions(
- Manifest.permission.WRITE_DEVICE_CONFIG,
- Manifest.permission.READ_DEVICE_CONFIG);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
Integer.toString(3), /*makeDefault*/false);
@@ -933,11 +932,9 @@
// Raise 2 failures at t=0 and t=900 respectively
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
- mTestLooper.dispatchAll();
moveTimeForwardAndDispatch(900);
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
- mTestLooper.dispatchAll();
// Raise 2 failures at t=1100
moveTimeForwardAndDispatch(200);
@@ -1303,15 +1300,15 @@
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED,
Boolean.toString(enabled), /*makeDefault*/false);
- //give time for DeviceConfig to broadcast the property value change
- try {
- Thread.sleep(SHORT_DURATION);
- } catch (InterruptedException e) {
- fail("Thread.sleep unexpectedly failed!");
+ // Call updateConfigs() so device config changes take effect immediately
+ for (PackageWatchdog watchdog : mAllocatedWatchdogs) {
+ watchdog.updateConfigs();
}
}
private void moveTimeForwardAndDispatch(long milliSeconds) {
+ // Exhaust all due runnables now which shouldn't be executed after time-leap
+ mTestLooper.dispatchAll();
mTestClock.moveTimeForward(milliSeconds);
mTestLooper.moveTimeForward(milliSeconds);
mTestLooper.dispatchAll();
@@ -1354,6 +1351,7 @@
verify(mConnectivityModuleConnector).registerHealthListener(
mConnectivityModuleCallbackCaptor.capture());
}
+ mAllocatedWatchdogs.add(watchdog);
return watchdog;
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 0db2b2a..7b2a07f 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -1225,4 +1225,44 @@
InstallUtils.dropShellPermissionIdentity();
}
}
+
+ /**
+ * Tests an app can be rolled back to the previous signing key.
+ *
+ * <p>The rollback capability in the signing lineage allows an app to be updated to an APK
+ * signed with a previous signing key in the lineage; however this often defeats the purpose
+ * of key rotation as a compromised key could then be used to roll an app back to the previous
+ * key. To avoid requiring the rollback capability to support app rollbacks the PackageManager
+ * allows an app to be rolled back to the previous signing key if the rollback install reason
+ * is set.
+ */
+ @Test
+ public void testRollbackAfterKeyRotation() throws Exception {
+ try {
+ InstallUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
+ Manifest.permission.MANAGE_ROLLBACKS);
+
+ // Uninstall TestApp.A
+ Uninstall.packages(TestApp.A);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+
+ // Install v1 of the app with the original signing key (without rollbacks enabled).
+ Install.single(TestApp.AOriginal1).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+
+ // Upgrade from v1 to v2 with the rotated signing key, with rollbacks enabled.
+ Install.single(TestApp.ARotated2).setEnableRollback().commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+
+ // Roll back the app.
+ RollbackInfo available = waitForAvailableRollback(TestApp.A);
+ RollbackUtils.rollback(available.getRollbackId());
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ } finally {
+ InstallUtils.dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
new file mode 100644
index 0000000..d809fe8
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+java_test_host {
+ name: "UpdatableSystemFontTest",
+ srcs: ["src/**/*.java"],
+ libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
+ static_libs: ["frameworks-base-hostutils"],
+ test_suites: ["general-tests", "vts"],
+ data: [
+ ":NotoColorEmojiTtf",
+ ":UpdatableSystemFontTestCertDer",
+ ":UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
+ ":UpdatableSystemFontTestNotoColorEmojiV1Ttf",
+ ":UpdatableSystemFontTestNotoColorEmojiV1TtfFsvSig",
+ ":UpdatableSystemFontTestNotoColorEmojiV2Ttf",
+ ":UpdatableSystemFontTestNotoColorEmojiV2TtfFsvSig",
+ ],
+}
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
new file mode 100644
index 0000000..efe5d70
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+<configuration description="Updatable system font integration/regression test">
+ <option name="test-suite-tag" value="apct" />
+
+ <!-- This test requires root to side load fs-verity cert. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
+ <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV1.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV2.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf" />
+ <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig" />
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="UpdatableSystemFontTest.jar" />
+ </test>
+</configuration>
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
new file mode 100644
index 0000000..6d161a5
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.updatablesystemfont;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.RootPermissionTest;
+
+import com.android.fsverity.AddFsVerityCertRule;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Tests if fonts can be updated by 'cmd font'.
+ */
+@RootPermissionTest
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
+
+ private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der";
+
+ private static final Pattern PATTERN_FONT = Pattern.compile("path = ([^, \n]*)");
+ private static final String NOTO_COLOR_EMOJI_TTF = "NotoColorEmoji.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V1_TTF =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig";
+ private static final String TEST_NOTO_COLOR_EMOJI_V2_TTF =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig";
+ private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF =
+ "/data/local/tmp/NotoColorEmoji.ttf";
+ private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG =
+ "/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig";
+
+ @Rule
+ public final AddFsVerityCertRule mAddFsverityCertRule =
+ new AddFsVerityCertRule(this, CERT_PATH);
+
+ @Before
+ public void setUp() throws Exception {
+ expectRemoteCommandToSucceed("cmd font clear");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ expectRemoteCommandToSucceed("cmd font clear");
+ }
+
+ @Test
+ public void updateFont() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath).startsWith("/data/fonts/files/");
+ }
+
+ @Test
+ public void updateFont_twice() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V2_TTF, TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG));
+ String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath2).startsWith("/data/fonts/files/");
+ assertThat(fontPath2).isNotEqualTo(fontPath);
+ }
+
+ @Test
+ public void updatedFont_dataFileIsImmutableAndReadable() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath).startsWith("/data");
+
+ expectRemoteCommandToFail("echo -n '' >> " + fontPath);
+ expectRemoteCommandToSucceed("cat " + fontPath + " > /dev/null");
+ }
+
+ @Test
+ public void updateFont_invalidCert() throws Exception {
+ expectRemoteCommandToFail(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG));
+ }
+
+ @Test
+ public void updateFont_downgradeFromSystem() throws Exception {
+ expectRemoteCommandToFail(String.format("cmd font update %s %s",
+ ORIGINAL_NOTO_COLOR_EMOJI_TTF, ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG));
+ }
+
+ @Test
+ public void updateFont_downgradeFromData() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V2_TTF, TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG));
+ expectRemoteCommandToFail(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ }
+
+ private String getFontPath(String fontFileName) throws Exception {
+ // TODO: add a dedicated command for testing.
+ String lines = expectRemoteCommandToSucceed("cmd font dump");
+ for (String line : lines.split("\n")) {
+ Matcher m = PATTERN_FONT.matcher(line);
+ if (m.find() && m.group(1).endsWith(fontFileName)) {
+ return m.group(1);
+ }
+ }
+ CLog.e("Font not found: " + fontFileName);
+ return null;
+ }
+
+ private String expectRemoteCommandToSucceed(String cmd) throws Exception {
+ CommandResult result = getDevice().executeShellV2Command(cmd);
+ assertWithMessage("`" + cmd + "` failed: " + result.getStderr())
+ .that(result.getStatus())
+ .isEqualTo(CommandStatus.SUCCESS);
+ return result.getStdout();
+ }
+
+ private void expectRemoteCommandToFail(String cmd) throws Exception {
+ CommandResult result = getDevice().executeShellV2Command(cmd);
+ assertWithMessage("Unexpected success from `" + cmd + "`: " + result.getStderr())
+ .that(result.getStatus())
+ .isNotEqualTo(CommandStatus.SUCCESS);
+ }
+}
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
new file mode 100644
index 0000000..1296699
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -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.
+
+filegroup {
+ name: "UpdatableSystemFontTestKeyPem",
+ srcs: ["UpdatableSystemFontTestKey.pem"],
+}
+
+filegroup {
+ name: "UpdatableSystemFontTestCertPem",
+ srcs: ["UpdatableSystemFontTestCert.pem"],
+}
+
+filegroup {
+ name: "UpdatableSystemFontTestCertDer",
+ srcs: ["UpdatableSystemFontTestCert.der"],
+}
+
+genrule_defaults {
+ name: "updatable_system_font_increment_font_revision_default",
+ tools: ["update_font_metadata"],
+ cmd: "$(location update_font_metadata) " +
+ "--input=$(in) " +
+ "--output=$(out) " +
+ "--revision=+1",
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiV1Ttf",
+ defaults: ["updatable_system_font_increment_font_revision_default"],
+ srcs: [":NotoColorEmojiTtf"],
+ out: ["UpdatableSystemFontTestNotoColorEmojiV1.ttf"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiV2Ttf",
+ defaults: ["updatable_system_font_increment_font_revision_default"],
+ srcs: [":UpdatableSystemFontTestNotoColorEmojiV1Ttf"],
+ out: ["UpdatableSystemFontTestNotoColorEmojiV2.ttf"],
+}
+
+genrule_defaults {
+ name: "updatable_system_font_sig_gen_default",
+ tools: ["fsverity"],
+ tool_files: [":UpdatableSystemFontTestKeyPem", ":UpdatableSystemFontTestCertPem"],
+ cmd: "$(location fsverity) sign $(in) $(out) " +
+ "--key=$(location :UpdatableSystemFontTestKeyPem) " +
+ "--cert=$(location :UpdatableSystemFontTestCertPem) " +
+ "> /dev/null",
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":NotoColorEmojiTtf"],
+ out: ["UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiV1TtfFsvSig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":UpdatableSystemFontTestNotoColorEmojiV1Ttf"],
+ out: ["UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTestNotoColorEmojiV2TtfFsvSig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":UpdatableSystemFontTestNotoColorEmojiV2Ttf"],
+ out: ["UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig"],
+}
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.der b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.der
new file mode 100644
index 0000000..f7aa15f
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.der
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.pem b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.pem
new file mode 100644
index 0000000..0cd1f66
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFOTCCAyGgAwIBAgIUFaI1D5NtwkCVM3G4bFZ6sQSb598wDQYJKoZIhvcNAQEL
+BQAwLDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRAwDgYDVQQKDAdBbmRyb2lk
+MB4XDTIxMDIwMTA3MzAyNFoXDTIxMDMwMzA3MzAyNFowLDELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMRAwDgYDVQQKDAdBbmRyb2lkMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA0U1zptc41E65ooeBPD33Mjgp6cYPydyj2Acq80Xy7lP7
+d6/2t6w7nNNl2x3n8dAhOl3de3IxTp6JI2SdoRb7obfcp+hWJoo/cxnHpr3q/u4R
+KED0rmWaOVHpGbajSTFZgN+cTbTKJbgtXm/H65x1QfO18ep/vj5fRiu1xPpJqDv/
+xuvuko2U3eC2+NayxzCWXVFrKPLx8GvzSQ3Utaug17vs7/5GqkRJgq3lk4DvmjNA
+vY8YA4RAkII1sSaceAWFEG6ztENLu2kjcxAI9qHxxBwQZit/NtFVlFGqSN3MEYjS
+M9Fz04RsUxF672QJpAgwCJDZ41rdB3hkHvOUK9PcepBsHdZq9cQ+E64+TX+jsJLu
+VouViKlYr6WYjvhfqZeRhwbj7CoEZ2DyEZKrl27fgWaidUT5LGEQLVxg90ymbimI
+6UwXRUwmRBQJBdRO4RGvngtqxRuamyjAKDDHx5YccXCX4FWLUypyQlz0asojvbJZ
+Og7DFa1qsRdGrGIRoQJ8pYnAjBJfSudr1l0mR7fZSfZc0W9ZmuROWx9Ip7aJWQnQ
+8JLtbNPuFLD2qbmg9Y1lcXJp1FvI9FcM8JsBqZNEANQwwsdTaa8gw+3W6J2SXKQP
+H+yZI/fJWWWRFADtqmpxtvXK9K+Cy1HQmg7D0IIxVPp8rrbz6TGrg+R3/N9FBWMC
+AwEAAaNTMFEwHQYDVR0OBBYEFDS48o2UAstoOyLMcgamHKrZdl3cMB8GA1UdIwQY
+MBaAFDS48o2UAstoOyLMcgamHKrZdl3cMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBAF7taBYAe20tWZu0pY9d4Z8il4LoJcRrKF4YiA02UizErgCF
+h4iECy6+pcu7DJUfvCh3dCWE7CDG+OnfUWTwEHVG9n8XI/ydetBUG76PZwTadI7B
+gzJ1y7/vWqJo5U6ki+sXNmq3hkgNsNZgza3LpdovkWJYeRdffM6m/bimzwYx9id8
+5mKw2PcbVZcb25r+0dCoLVJsqqCoRjdYUy/MKPutWG2bPzmaIv8KsKFN+mzlwhJH
+lpJ/LR+3NoaHrOCFG7CW/2Ihe501vmdQ2m/VKosyk0igw8WmTsY6xMbw2t77yKkD
+hnJr1NbhKeEV9gAB2BFX8nRWI7NTgp8fG78YLVz1UcbIHmYLgFoc3ezyma+CoR86
+ER20lKd4+TNnz4RtaPdZlBa0Ba3bsMtEneqlrHvcPrZ5tgGsQR9+cy3ZtTZ/LUQX
++Xuj/EoJXuuB3hkhg52zawN5n7WUe8efWHcv1jHqeIj0phcgbZ6u4fFBPsYjzDKe
+VuYHXglNOchmoBQwEaJI/TCiEgI8dcSJXSquLAXrtznVnxzT46ZMEt5LaW1/1NLx
+q//yoPdolCI0lpunh5jvIZJpUl5XMjxVSyaveQDNVqJkITWzWqIxAT5yTLtkCNlW
+c1XyzeHkpMItiJtBruExmnaTmNjlVKsXP8wQFOYbDGgXY5iHIMbgovptRyH/
+-----END CERTIFICATE-----
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestKey.pem b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestKey.pem
new file mode 100644
index 0000000..09bb104
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestKey.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDRTXOm1zjUTrmi
+h4E8PfcyOCnpxg/J3KPYByrzRfLuU/t3r/a3rDuc02XbHefx0CE6Xd17cjFOnokj
+ZJ2hFvuht9yn6FYmij9zGcemver+7hEoQPSuZZo5UekZtqNJMVmA35xNtMoluC1e
+b8frnHVB87Xx6n++Pl9GK7XE+kmoO//G6+6SjZTd4Lb41rLHMJZdUWso8vHwa/NJ
+DdS1q6DXu+zv/kaqREmCreWTgO+aM0C9jxgDhECQgjWxJpx4BYUQbrO0Q0u7aSNz
+EAj2ofHEHBBmK3820VWUUapI3cwRiNIz0XPThGxTEXrvZAmkCDAIkNnjWt0HeGQe
+85Qr09x6kGwd1mr1xD4Trj5Nf6Owku5Wi5WIqVivpZiO+F+pl5GHBuPsKgRnYPIR
+kquXbt+BZqJ1RPksYRAtXGD3TKZuKYjpTBdFTCZEFAkF1E7hEa+eC2rFG5qbKMAo
+MMfHlhxxcJfgVYtTKnJCXPRqyiO9slk6DsMVrWqxF0asYhGhAnylicCMEl9K52vW
+XSZHt9lJ9lzRb1ma5E5bH0intolZCdDwku1s0+4UsPapuaD1jWVxcmnUW8j0Vwzw
+mwGpk0QA1DDCx1NpryDD7dbonZJcpA8f7Jkj98lZZZEUAO2qanG29cr0r4LLUdCa
+DsPQgjFU+nyutvPpMauD5Hf830UFYwIDAQABAoICAQCTVTcFCdl6MdSg4UwK0P/S
+fRCb/A0fJs67Agis6N9h/wI0NUyx7G6mLXU0si+U29KYGH0RKcgltJmKrYf8XoZR
+R3DvTTBfvs99QXd2G5hxTboMIPVcUi8nDE7PB+6XVkLP4hhP5uSpeqWNJZiQdTlh
+bKH2IgE8NQGyDpDMkPcKkvmw2GG/DiTtrwJ91fxRFRWzqN2LHMFMYWEHWtIR9Der
+xSC7q72om5s3fxvtIkUHwe5fwXvA9fbRAqezBR/9qL0LXTHowbpsuUz38SCuJD9g
+sfSlRxcsyly4pGf/FQpSiYKWcWlcSopKSzLDkyLqMc1GKlkGnu6aFJg95W63D1LS
+OaOXuYShHxLkqyhT8uQGRqDCu3E2ivb6fMxAPzJxxs3JrZvumNsqyxbp+HVF0idj
+NijMN/8Kb4KmNHG9I3SHG61tQFYDtxoMMNiHzq3fafBJnVcf6iThQdE5pGLN2OdF
+3rcSTeHI2HxhTrXtuiHmWXNk9aZ2TOhrssNZZjDkFL/KYh6G8guy+tn66YWy77VC
+id+6PBzYXTXsUauo4NWW1rLUfzT/y93IwVGpoXs7GHUUduZ6Q3PxsMTMF9IjBvqR
+JvfP84CUfGXoebVJmWyGhtW6N5ParvQxbonDitA0TPZcRyX3N8yqIGO/mb8MWA9M
+7s/xMZuksOpw5LSCoTAW6QKCAQEA9Z1WM/5XT+5HkwPBvS5aRz0SqMqPxAkZ0dNz
+O06zpXv6e2IPy40UzFWCIkyq3vWKQ5bqU1fnejUdmjvtnP+KhH6fxnQCgiunnrDq
+j1Sk2Y4gb1KyZY/C8IejOexM2qX7sfDTLI8XEvxJxVFCNmvYfnv4AX5QD8VOsjrg
+bvodLAgDSo2FVDP+mkpW7zAIoRV02l6QdZA+YcG940eqxB9sPR3/1KUHe9wUTTbJ
+FV5ahEPuXCyvRJkZ/rD5CPPZoQHfjKHxDlu7yfoatzCSYj10R+RInfqOFGVY4D/C
+2csXjymwTN4CFUnJcP410YhPFn5ekmc/E3xqPKgIDQhQ2+oivwKCAQEA2icN1iEo
+YuBJwB3pX3jrwk+1bpUXASueWhyAhSeNMrTrJ/lSgEAy9FBxJNnK1PjkABRnFhS+
+uxbC2hQdfAkNDS21PQOk6hOhebUVuBdfmKY1CL+P9y4Af0fjV1rgRGHihnZB5lKU
+1R/wFb8c4QgPwduiqDoZ5QP3dgxpZ4R3SCzuoyVelUSlgyWPoslg+yPddcnV8bTf
+BUlEOOyXgVudkSFlRpWZ+/ZAkLTj8rnrtKp8/+GtTQvJvPJHPfuSsDhI+EQVWqab
+HelMhxvIi9xOyN/OCc3Ex6JlEVa+X8EyNhQsV2sAKnld92hdEozW2uxRniIkxvL4
+CuBp/p3fWxEaXQKCAQEAw2j7RYCMnNZZ8Zhiko4HW3g2mT4XpYMMHMlbe4sBGJ8L
+yRBaurqzGmLJl1ph8+NsrpuqMMbWLn+F3sjhIjCZVxKbMbvopwHuaS4eYAya30/Z
+dFhaAL2g/dccQSBEgQzftFGC4YeydvNsCeW9hSjGZNNinGWPcwyqsNhw6Tpq7TYu
+0CjKNBTt8nlEsyYHJ4m3n2jvC+nIB+Spm+LP9Rt+9R0iBl+KFbwiFtCIqUyZPXQC
+dylCBJS+fsj0SXAg7J1d6ziIXcEUJfyrNqYZQLneAriYIcBPO+DqFfgEoVyYkNk9
+H9rd02wSLajCzsLhEWdW/KnSIEGzEDErvpqoIl8kZwKCAQEAjNO3T+sZyjKmCXqF
+xBcogsi4BAoEzsGcuOk7Yjn1Ia2/PI/r3VUUT7l6QOLD2JZPgWmqXovH0LjR0rw3
+iHHDViWSoS+wD1fa3tmyiqO0F7P7+ojHZDbzJTeAIE1PB3X1KP5AbnITGD5E25UD
+DJYKrgeeSmEvhDL6Vd+PT78ozZQL/Y/LLishebcOsXS0wYsWlMpV7XHoot34R5Mb
+/urond7kJRvASvJeHcxYdsHk0j1Y8kp6eIlKk0oICZBU0qOTH4m8C0gQTM/lkjay
+UO9IgM5RkOyfwowoGHhZ7zClvFlrgodVlRXCPkvGAYqfzLXPvnimKzSAQW07n53E
+qWIyFQKCAQAWRNG6hPCOzkNxMJ/RK7dwxZW6b4a1L3PMXTT/xrBKRIS1WNjbpkYO
+/FLIufOqJT6FQN2obM5uso3TI+R7MwH8DnTSnDDy0Hvs3CdHdtn2tapZOViF/UVv
+uCQa+/jMVKFCZ8k7pPFMIG6tB6WBA5MmJrW+8s0ouxLbRF5rZyzqOeyPdVBYYYDb
+68nGNA6GAtTQs9h7xV2tsQ1bXNP+6gqG3BgZYo+76xKITddT6s9aaC++LVBnPOdq
+LHV7gUvoBkVLjIp4L3Fb/DGMCcOVMCxmFlRBn+RBlV7slehvgq+Ywz2GHWLr+O/z
+V2NAtwvCfZE2Do/4f2mpHnamhS6AvrDe
+-----END PRIVATE KEY-----
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
index ef973ac..861d221 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -182,6 +182,24 @@
@SmallTest
@Test
+ public void setFunctionsNcmAndRndis() {
+ final long rndisPlusNcm = UsbManager.FUNCTION_RNDIS | UsbManager.FUNCTION_NCM;
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_NCM));
+ assertEquals(UsbManager.FUNCTION_NCM, mUsbHandler.getEnabledFunctions() & rndisPlusNcm);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ rndisPlusNcm));
+ assertEquals(rndisPlusNcm, mUsbHandler.getEnabledFunctions() & rndisPlusNcm);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_NCM));
+ assertEquals(UsbManager.FUNCTION_NCM, mUsbHandler.getEnabledFunctions() & rndisPlusNcm);
+ }
+
+ @SmallTest
+ @Test
public void enableAdb() {
sendBootCompleteMessages(mUsbHandler);
Message msg = mUsbHandler.obtainMessage(MSG_ENABLE_ADB);
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java b/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java
index a0fd9d4..b8bd98e 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import static org.junit.Assert.assertEquals;
+
import android.content.Context;
import android.hardware.usb.UsbManager;
@@ -23,12 +25,12 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.usblib.UsbManagerTestLib;
+
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
-import com.android.server.usblib.UsbManagerTestLib;
-
/**
* Unit tests for {@link android.hardware.usb.UsbManager}.
* Note: NOT claimed MANAGE_USB permission in Manifest
@@ -78,4 +80,35 @@
public void testUsbApi_SetCurrentFunctions_OnSecurityException() throws Exception {
mUsbManagerTestLib.testSetCurrentFunctionsEx(UsbManager.FUNCTION_NONE);
}
+
+ public void assertSettableFunctions(boolean settable, long functions) {
+ assertEquals(
+ "areSettableFunctions(" + UsbManager.usbFunctionsToString(functions) + "):",
+ settable, UsbManager.areSettableFunctions(functions));
+ }
+
+ /**
+ * Tests the behaviour of the static areSettableFunctions method. This method performs no IPCs
+ * and requires no permissions.
+ */
+ @Test
+ public void testUsbManager_AreSettableFunctions() {
+ // NONE is settable.
+ assertSettableFunctions(true, UsbManager.FUNCTION_NONE);
+
+ // MTP, PTP, RNDIS, MIDI, NCM are all settable by themselves.
+ assertSettableFunctions(true, UsbManager.FUNCTION_MTP);
+ assertSettableFunctions(true, UsbManager.FUNCTION_PTP);
+ assertSettableFunctions(true, UsbManager.FUNCTION_RNDIS);
+ assertSettableFunctions(true, UsbManager.FUNCTION_MIDI);
+ assertSettableFunctions(true, UsbManager.FUNCTION_NCM);
+
+ // Setting two functions at the same time is not allowed...
+ assertSettableFunctions(false, UsbManager.FUNCTION_MTP | UsbManager.FUNCTION_PTP);
+ assertSettableFunctions(false, UsbManager.FUNCTION_PTP | UsbManager.FUNCTION_RNDIS);
+ assertSettableFunctions(false, UsbManager.FUNCTION_MIDI | UsbManager.FUNCTION_NCM);
+
+ // ... except in the special case of RNDIS and NCM.
+ assertSettableFunctions(true, UsbManager.FUNCTION_RNDIS | UsbManager.FUNCTION_NCM);
+ }
}
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index f6a2846..ffde68e 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -36,7 +36,7 @@
"libvndksupport",
"libziparchive",
"libz",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-V5-cpp",
],
}
@@ -53,6 +53,7 @@
jarjar_rules: "jarjar-rules.txt",
static_libs: [
"androidx.test.rules",
+ "bouncycastle-repackaged-unbundled",
"FrameworksNetCommonTests",
"frameworks-base-testutils",
"frameworks-net-integration-testutils",
diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
new file mode 100644
index 0000000..87cfb34
--- /dev/null
+++ b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net
+
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.assertParcelSane
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+
+private const val TEST_OWNER_UID = 123
+private const val TEST_IFACE = "test_tun0"
+private val TEST_IFACE_LIST = listOf("wlan0", "rmnet_data0", "eth0")
+
+@SmallTest
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+class UnderlyingNetworkInfoTest {
+ @Test
+ fun testParcelUnparcel() {
+ val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST)
+ assertEquals(TEST_OWNER_UID, testInfo.ownerUid)
+ assertEquals(TEST_IFACE, testInfo.iface)
+ assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces)
+ assertParcelSane(testInfo, 3)
+
+ val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf())
+ assertEquals(0, emptyInfo.ownerUid)
+ assertEquals(String(), emptyInfo.iface)
+ assertEquals(listOf(), emptyInfo.underlyingIfaces)
+ assertParcelSane(emptyInfo, 3)
+ }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index f2dd27e..c2fddf3 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -32,6 +32,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
import static android.net.NetworkRequest.Type.REQUEST;
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
@@ -368,6 +369,12 @@
eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
eq(testPkgName), eq(null));
reset(mService);
+
+ manager.requestBackgroundNetwork(request, null, callback);
+ verify(mService).requestNetwork(eq(request.networkCapabilities),
+ eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+ eq(testPkgName), eq(null));
+ reset(mService);
}
static Message makeMessage(NetworkRequest req, int messageType) {
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
index 076e41d..1abd39a 100644
--- a/tests/net/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -30,7 +30,7 @@
import com.android.internal.net.VpnProfile;
import com.android.net.module.util.ProxyUtils;
-import com.android.org.bouncycastle.x509.X509V1CertificateGenerator;
+import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator;
import org.junit.Before;
import org.junit.Test;
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 137543a..c7554f6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -132,6 +132,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.NotificationManager;
@@ -201,8 +202,8 @@
import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.UidRangeParcel;
+import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
-import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
@@ -252,6 +253,7 @@
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.net.module.util.ArrayTrackRecord;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.MockableSystemProperties;
@@ -380,6 +382,10 @@
private QosCallbackMockHelper mQosCallbackMockHelper;
private QosCallbackTracker mQosCallbackTracker;
+ // State variables required to emulate NetworkPolicyManagerService behaviour.
+ private int mUidRules = RULE_NONE;
+ private boolean mRestrictBackground = false;
+
@Mock DeviceIdleInternal mDeviceIdleInternal;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
@@ -902,28 +908,69 @@
}
/**
- * A NetworkFactory that allows tests to wait until any in-flight NetworkRequest add or remove
- * operations have been processed. Before ConnectivityService can add or remove any requests,
- * the factory must be told to expect those operations by calling expectAddRequestsWithScores or
- * expectRemoveRequests.
+ * A NetworkFactory that allows to wait until any in-flight NetworkRequest add or remove
+ * operations have been processed and test for them.
*/
private static class MockNetworkFactory extends NetworkFactory {
private final ConditionVariable mNetworkStartedCV = new ConditionVariable();
private final ConditionVariable mNetworkStoppedCV = new ConditionVariable();
private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false);
- // Used to expect that requests be removed or added on a separate thread, without sleeping.
- // Callers can call either expectAddRequestsWithScores() or expectRemoveRequests() exactly
- // once, then cause some other thread to add or remove requests, then call
- // waitForRequests().
- // It is not possible to wait for both add and remove requests. When adding, the queue
- // contains the expected score. When removing, the value is unused, all matters is the
- // number of objects in the queue.
- private final LinkedBlockingQueue<Integer> mExpectations;
+ static class RequestEntry {
+ @NonNull
+ public final NetworkRequest request;
- // Whether we are currently expecting requests to be added or removed. Valid only if
- // mExpectations is non-empty.
- private boolean mExpectingAdditions;
+ RequestEntry(@NonNull final NetworkRequest request) {
+ this.request = request;
+ }
+
+ static final class Add extends RequestEntry {
+ public final int factorySerialNumber;
+
+ Add(@NonNull final NetworkRequest request, final int factorySerialNumber) {
+ super(request);
+ this.factorySerialNumber = factorySerialNumber;
+ }
+ }
+
+ static final class Remove extends RequestEntry {
+ Remove(@NonNull final NetworkRequest request) {
+ super(request);
+ }
+ }
+ }
+
+ // History of received requests adds and removes.
+ private final ArrayTrackRecord<RequestEntry>.ReadHead mRequestHistory =
+ new ArrayTrackRecord<RequestEntry>().newReadHead();
+
+ private static <T> T failIfNull(@Nullable final T obj, @Nullable final String message) {
+ if (null == obj) fail(null != message ? message : "Must not be null");
+ return obj;
+ }
+
+
+ public RequestEntry.Add expectRequestAdd() {
+ return failIfNull((RequestEntry.Add) mRequestHistory.poll(TIMEOUT_MS,
+ it -> it instanceof RequestEntry.Add), "Expected request add");
+ }
+
+ public void expectRequestAdds(final int count) {
+ for (int i = count; i > 0; --i) {
+ expectRequestAdd();
+ }
+ }
+
+ public RequestEntry.Remove expectRequestRemove() {
+ return failIfNull((RequestEntry.Remove) mRequestHistory.poll(TIMEOUT_MS,
+ it -> it instanceof RequestEntry.Remove), "Expected request remove");
+ }
+
+ public void expectRequestRemoves(final int count) {
+ for (int i = count; i > 0; --i) {
+ expectRequestRemove();
+ }
+ }
// Used to collect the networks requests managed by this factory. This is a duplicate of
// the internal information stored in the NetworkFactory (which is private).
@@ -932,7 +979,6 @@
public MockNetworkFactory(Looper looper, Context context, String logTag,
NetworkCapabilities filter) {
super(looper, context, logTag, filter);
- mExpectations = new LinkedBlockingQueue<>();
}
public int getMyRequestCount() {
@@ -966,95 +1012,33 @@
@Override
protected void handleAddRequest(NetworkRequest request, int score,
int factorySerialNumber) {
- synchronized (mExpectations) {
- final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
-
- assertNotNull("Added more requests than expected (" + request + " score : "
- + score + ")", expectedScore);
- // If we're expecting anything, we must be expecting additions.
- if (!mExpectingAdditions) {
- fail("Can't add requests while expecting requests to be removed");
- }
- if (expectedScore != score) {
- fail("Expected score was " + expectedScore + " but actual was " + score
- + " in added request");
- }
-
- // Add the request.
- mNetworkRequests.put(request.requestId, request);
- super.handleAddRequest(request, score, factorySerialNumber);
- mExpectations.notify();
- }
+ mNetworkRequests.put(request.requestId, request);
+ super.handleAddRequest(request, score, factorySerialNumber);
+ mRequestHistory.add(new RequestEntry.Add(request, factorySerialNumber));
}
@Override
protected void handleRemoveRequest(NetworkRequest request) {
- synchronized (mExpectations) {
- final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
+ mNetworkRequests.remove(request.requestId);
+ super.handleRemoveRequest(request);
+ mRequestHistory.add(new RequestEntry.Remove(request));
+ }
- assertTrue("Removed more requests than expected", expectedScore != null);
- // If we're expecting anything, we must be expecting removals.
- if (mExpectingAdditions) {
- fail("Can't remove requests while expecting requests to be added");
- }
+ public void assertRequestCountEquals(final int count) {
+ assertEquals(count, getMyRequestCount());
+ }
- // Remove the request.
- mNetworkRequests.remove(request.requestId);
- super.handleRemoveRequest(request);
- mExpectations.notify();
- }
+ @Override
+ public void terminate() {
+ super.terminate();
+ // Make sure there are no remaining requests unaccounted for.
+ assertNull(mRequestHistory.poll(TIMEOUT_MS, r -> true));
}
// Trigger releasing the request as unfulfillable
public void triggerUnfulfillable(NetworkRequest r) {
super.releaseRequestAsUnfulfillableByAnyFactory(r);
}
-
- private void assertNoExpectations() {
- if (mExpectations.size() != 0) {
- fail("Can't add expectation, " + mExpectations.size() + " already pending");
- }
- }
-
- // Expects that requests with the specified scores will be added.
- public void expectAddRequestsWithScores(final int... scores) {
- assertNoExpectations();
- mExpectingAdditions = true;
- for (int score : scores) {
- mExpectations.add(score);
- }
- }
-
- // Expects that count requests will be removed.
- public void expectRemoveRequests(final int count) {
- assertNoExpectations();
- mExpectingAdditions = false;
- for (int i = 0; i < count; ++i) {
- mExpectations.add(0); // For removals the score is ignored so any value will do.
- }
- }
-
- // Waits for the expected request additions or removals to happen within a timeout.
- public void waitForRequests() throws InterruptedException {
- final long deadline = SystemClock.elapsedRealtime() + TIMEOUT_MS;
- synchronized (mExpectations) {
- while (mExpectations.size() > 0 && SystemClock.elapsedRealtime() < deadline) {
- mExpectations.wait(deadline - SystemClock.elapsedRealtime());
- }
- }
- final long count = mExpectations.size();
- final String msg = count + " requests still not " +
- (mExpectingAdditions ? "added" : "removed") +
- " after " + TIMEOUT_MS + " ms";
- assertEquals(msg, 0, count);
- }
-
- public SparseArray<NetworkRequest> waitForNetworkRequests(final int count)
- throws InterruptedException {
- waitForRequests();
- assertEquals(count, getMyRequestCount());
- return mNetworkRequests;
- }
}
private Set<UidRange> uidRangesForUid(int uid) {
@@ -1076,7 +1060,7 @@
private boolean mAgentRegistered = false;
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
- private VpnInfo mVpnInfo;
+ private UnderlyingNetworkInfo mUnderlyingNetworkInfo;
// These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started.
// TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the
@@ -1250,14 +1234,15 @@
}
@Override
- public synchronized VpnInfo getVpnInfo() {
- if (mVpnInfo != null) return mVpnInfo;
+ public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() {
+ if (mUnderlyingNetworkInfo != null) return mUnderlyingNetworkInfo;
- return super.getVpnInfo();
+ return super.getUnderlyingNetworkInfo();
}
- private synchronized void setVpnInfo(VpnInfo vpnInfo) {
- mVpnInfo = vpnInfo;
+ private synchronized void setUnderlyingNetworkInfo(
+ UnderlyingNetworkInfo underlyingNetworkInfo) {
+ mUnderlyingNetworkInfo = underlyingNetworkInfo;
}
}
@@ -1277,12 +1262,23 @@
}
}
+ private void updateUidNetworkingBlocked() {
+ doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked(
+ i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */,
+ mRestrictBackground)
+ ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
+ }
+
private void setUidRulesChanged(int uidRules) throws RemoteException {
- mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+ mUidRules = uidRules;
+ updateUidNetworkingBlocked();
+ mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
}
private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
- mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+ mRestrictBackground = restrictBackground;
+ updateUidNetworkingBlocked();
+ mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
}
private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) {
@@ -2579,12 +2575,6 @@
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
}
- private int[] makeIntArray(final int size, final int value) {
- final int[] array = new int[size];
- Arrays.fill(array, value);
- return array;
- }
-
private void tryNetworkFactoryRequests(int capability) throws Exception {
// Verify NOT_RESTRICTED is set appropriately
final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability)
@@ -2606,9 +2596,9 @@
mServiceContext, "testFactory", filter);
testFactory.setScoreFilter(40);
ConditionVariable cv = testFactory.getNetworkStartedCV();
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
int expectedRequestCount = 1;
NetworkCallback networkCallback = null;
// For non-INTERNET capabilities we cannot rely on the default request being present, so
@@ -2617,13 +2607,12 @@
assertFalse(testFactory.getMyStartRequested());
NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build();
networkCallback = new NetworkCallback();
- testFactory.expectAddRequestsWithScores(0); // New request
mCm.requestNetwork(request, networkCallback);
expectedRequestCount++;
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdd();
}
waitFor(cv);
- assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertTrue(testFactory.getMyStartRequested());
// Now bring in a higher scored network.
@@ -2637,15 +2626,14 @@
// When testAgent connects, ConnectivityService will re-send us all current requests with
// the new score. There are expectedRequestCount such requests, and we must wait for all of
// them.
- testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 50));
testAgent.connect(false);
testAgent.addCapability(capability);
waitFor(cv);
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdds(expectedRequestCount);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Bring in a bunch of requests.
- testFactory.expectAddRequestsWithScores(makeIntArray(10, 50));
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
ConnectivityManager.NetworkCallback[] networkCallbacks =
new ConnectivityManager.NetworkCallback[10];
@@ -2655,24 +2643,24 @@
builder.addCapability(capability);
mCm.requestNetwork(builder.build(), networkCallbacks[i]);
}
- testFactory.waitForNetworkRequests(10 + expectedRequestCount);
+ testFactory.expectRequestAdds(10);
+ testFactory.assertRequestCountEquals(10 + expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Remove the requests.
- testFactory.expectRemoveRequests(10);
for (int i = 0; i < networkCallbacks.length; i++) {
mCm.unregisterNetworkCallback(networkCallbacks[i]);
}
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestRemoves(10);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Drop the higher scored network.
cv = testFactory.getNetworkStartedCV();
- // With the default network disconnecting, the requests are sent with score 0 to factories.
- testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 0));
testAgent.disconnect();
waitFor(cv);
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdds(expectedRequestCount);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
assertTrue(testFactory.getMyStartRequested());
@@ -2715,9 +2703,8 @@
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter);
// Register the factory and don't be surprised when the default request arrives.
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
testFactory.setScoreFilter(42);
testFactory.terminate();
@@ -3693,10 +3680,13 @@
@Test
public void testBackgroundNetworks() throws Exception {
- // Create a background request. We can't do this ourselves because ConnectivityService
- // doesn't have an API for it. So just turn on mobile data always on.
- setAlwaysOnNetworks(true);
+ // Create a cellular background request.
grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
+ final TestNetworkCallback cellBgCallback = new TestNetworkCallback();
+ mCm.requestBackgroundNetwork(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build(), null, cellBgCallback);
+
+ // Make callbacks for monitoring.
final NetworkRequest request = new NetworkRequest.Builder().build();
final NetworkRequest fgRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_FOREGROUND).build();
@@ -3765,6 +3755,7 @@
mCm.unregisterNetworkCallback(callback);
mCm.unregisterNetworkCallback(fgCallback);
+ mCm.unregisterNetworkCallback(cellBgCallback);
}
@Ignore // This test has instrinsic chances of spurious failures: ignore for continuous testing.
@@ -3856,38 +3847,37 @@
testFactory.setScoreFilter(40);
// Register the factory and expect it to start looking for a network.
- testFactory.expectAddRequestsWithScores(0); // Score 0 as the request is not served yet.
testFactory.register();
try {
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
assertTrue(testFactory.getMyStartRequested());
// Bring up wifi. The factory stops looking for a network.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
// Score 60 - 40 penalty for not validated yet, then 60 when it validates
- testFactory.expectAddRequestsWithScores(20, 60);
mWiFiNetworkAgent.connect(true);
- testFactory.waitForRequests();
+ // Default request and mobile always on request
+ testFactory.expectRequestAdds(2);
assertFalse(testFactory.getMyStartRequested());
- ContentResolver cr = mServiceContext.getContentResolver();
-
// Turn on mobile data always on. The factory starts looking again.
- testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0
setAlwaysOnNetworks(true);
- testFactory.waitForNetworkRequests(2);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(2);
+
assertTrue(testFactory.getMyStartRequested());
// Bring up cell data and check that the factory stops looking.
assertLength(1, mCm.getAllNetworks());
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- testFactory.waitForNetworkRequests(2);
- assertFalse(
- testFactory.getMyStartRequested()); // Because the cell network outscores us.
+ testFactory.expectRequestAdds(2); // Unvalidated and validated
+ testFactory.assertRequestCountEquals(2);
+ // The cell network outscores the factory filter, so start is not requested.
+ assertFalse(testFactory.getMyStartRequested());
// Check that cell data stays up.
waitForIdle();
@@ -3895,9 +3885,8 @@
assertLength(2, mCm.getAllNetworks());
// Turn off mobile data always on and expect the request to disappear...
- testFactory.expectRemoveRequests(1);
setAlwaysOnNetworks(false);
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestRemove();
// ... and cell data to be torn down.
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
@@ -4204,46 +4193,33 @@
testFactory.setScoreFilter(40);
// Register the factory and expect it to receive the default request.
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- SparseArray<NetworkRequest> requests = testFactory.waitForNetworkRequests(1);
-
- assertEquals(1, requests.size()); // have 1 request at this point
- int origRequestId = requests.valueAt(0).requestId;
+ testFactory.expectRequestAdd();
// Now file the test request and expect it.
- testFactory.expectAddRequestsWithScores(0);
mCm.requestNetwork(nr, networkCallback);
- requests = testFactory.waitForNetworkRequests(2); // have 2 requests at this point
+ final NetworkRequest newRequest = testFactory.expectRequestAdd().request;
- int newRequestId = 0;
- for (int i = 0; i < requests.size(); ++i) {
- if (requests.valueAt(i).requestId != origRequestId) {
- newRequestId = requests.valueAt(i).requestId;
- break;
- }
- }
-
- testFactory.expectRemoveRequests(1);
if (preUnregister) {
mCm.unregisterNetworkCallback(networkCallback);
// Simulate the factory releasing the request as unfulfillable: no-op since
// the callback has already been unregistered (but a test that no exceptions are
// thrown).
- testFactory.triggerUnfulfillable(requests.get(newRequestId));
+ testFactory.triggerUnfulfillable(newRequest);
} else {
// Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
- testFactory.triggerUnfulfillable(requests.get(newRequestId));
+ testFactory.triggerUnfulfillable(newRequest);
networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
- testFactory.waitForRequests();
// unregister network callback - a no-op (since already freed by the
// on-unavailable), but should not fail or throw exceptions.
mCm.unregisterNetworkCallback(networkCallback);
}
+ testFactory.expectRequestRemove();
+
testFactory.terminate();
handlerThread.quit();
}
@@ -5195,20 +5171,22 @@
private void expectForceUpdateIfaces(Network[] networks, String defaultIface,
Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class);
- ArgumentCaptor<VpnInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class);
+ ArgumentCaptor<UnderlyingNetworkInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(
+ UnderlyingNetworkInfo[].class);
verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(),
any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture());
assertSameElementsNoDuplicates(networksCaptor.getValue(), networks);
- VpnInfo[] infos = vpnInfosCaptor.getValue();
+ UnderlyingNetworkInfo[] infos = vpnInfosCaptor.getValue();
if (vpnUid != null) {
assertEquals("Should have exactly one VPN:", 1, infos.length);
- VpnInfo info = infos[0];
+ UnderlyingNetworkInfo info = infos[0];
assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid);
- assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface);
- assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces);
+ assertEquals("Unexpected VPN interface:", vpnIfname, info.iface);
+ assertSameElementsNoDuplicates(underlyingIfaces,
+ info.underlyingIfaces.toArray(new String[0]));
} else {
assertEquals(0, infos.length);
return;
@@ -5269,7 +5247,7 @@
waitForIdle();
verify(mStatsService, never())
.forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ eq(new UnderlyingNetworkInfo[0]));
reset(mStatsService);
// Roaming change should update ifaces
@@ -5352,8 +5330,8 @@
// network for the VPN...
verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class),
any(NetworkState[].class), any() /* anyString() doesn't match null */,
- argThat(infos -> infos[0].underlyingIfaces.length == 1
- && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0])));
+ argThat(infos -> infos[0].underlyingIfaces.size() == 1
+ && WIFI_IFNAME.equals(infos[0].underlyingIfaces.get(0))));
verifyNoMoreInteractions(mStatsService);
reset(mStatsService);
@@ -5366,8 +5344,8 @@
waitForIdle();
verify(mStatsService).forceUpdateIfaces(any(Network[].class),
any(NetworkState[].class), any() /* anyString() doesn't match null */,
- argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1
- && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0])));
+ argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.size() == 1
+ && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces.get(0))));
mEthernetNetworkAgent.disconnect();
waitForIdle();
reset(mStatsService);
@@ -6835,9 +6813,15 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
setUidRulesChanged(RULE_REJECT_ALL);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
// ConnectivityService should cache it not to invoke the callback again.
setUidRulesChanged(RULE_REJECT_METERED);
@@ -6845,32 +6829,60 @@
setUidRulesChanged(RULE_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
setUidRulesChanged(RULE_REJECT_METERED);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
// Restrict the network based on UID rule and NOT_METERED capability change.
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+
setUidRulesChanged(RULE_ALLOW_METERED);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
setUidRulesChanged(RULE_NONE);
cellNetworkCallback.assertNoCallback();
- // Restrict the network based on BackgroundRestricted.
+ // Restrict background data. Networking is not blocked because the network is unmetered.
setRestrictBackgroundChanged(true);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
setRestrictBackgroundChanged(true);
cellNetworkCallback.assertNoCallback();
- setRestrictBackgroundChanged(false);
+
+ setUidRulesChanged(RULE_ALLOW_METERED);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+
+ setRestrictBackgroundChanged(false);
cellNetworkCallback.assertNoCallback();
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
mCm.unregisterNetworkCallback(cellNetworkCallback);
}
@@ -8343,12 +8355,14 @@
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ mMockVpn.setVpnType(vpnType);
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
- mMockVpn.setVpnType(vpnType);
- final VpnInfo vpnInfo = new VpnInfo(vpnOwnerUid, null, null);
- mMockVpn.setVpnInfo(vpnInfo);
+ final UnderlyingNetworkInfo underlyingNetworkInfo =
+ new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<String>());
+ mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo);
+ when(mDeps.getConnectionOwnerUid(anyInt(), any(), any())).thenReturn(42);
}
private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -8397,8 +8411,7 @@
final int myUid = Process.myUid();
setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
@Test
@@ -8408,8 +8421,7 @@
mServiceContext.setPermission(
android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
@Test
@@ -8420,8 +8432,7 @@
mServiceContext.setPermission(
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 68aaaed..f478282 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -148,6 +148,7 @@
managedProfileA.profileGroupId = primaryUser.id;
}
+ static final Network EGRESS_NETWORK = new Network(101);
static final String EGRESS_IFACE = "wlan0";
static final String TEST_VPN_PKG = "com.testvpn.vpn";
private static final String TEST_VPN_SERVER = "1.2.3.4";
@@ -963,7 +964,7 @@
InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE);
lp.addRoute(defaultRoute);
- vpn.startLegacyVpn(vpnProfile, mKeyStore, lp);
+ vpn.startLegacyVpn(vpnProfile, mKeyStore, EGRESS_NETWORK, lp);
return vpn;
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
index 1b33930e..a058a46 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
@@ -33,7 +33,9 @@
import static org.junit.Assert.assertEquals;
import android.net.NetworkStats;
-import android.net.VpnInfo;
+import android.net.UnderlyingNetworkInfo;
+
+import java.util.Arrays;
/** Superclass with utilities for NetworkStats(Service|Factory)Test */
abstract class NetworkStatsBaseTest {
@@ -107,11 +109,11 @@
assertEquals("unexpected operations", operations, entry.operations);
}
- static VpnInfo createVpnInfo(String[] underlyingIfaces) {
+ static UnderlyingNetworkInfo createVpnInfo(String[] underlyingIfaces) {
return createVpnInfo(TUN_IFACE, underlyingIfaces);
}
- static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
- return new VpnInfo(UID_VPN, vpnIface, underlyingIfaces);
+ static UnderlyingNetworkInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
+ return new UnderlyingNetworkInfo(UID_VPN, vpnIface, Arrays.asList(underlyingIfaces));
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index 76647a6..f3ae9b0 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -36,7 +36,7 @@
import android.content.res.Resources;
import android.net.NetworkStats;
import android.net.TrafficStats;
-import android.net.VpnInfo;
+import android.net.UnderlyingNetworkInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -79,7 +79,7 @@
// related to networkStatsFactory is compiled to a minimal native library and loaded here.
System.loadLibrary("networkstatsfactorytestjni");
mFactory = new NetworkStatsFactory(mTestProc, false);
- mFactory.updateVpnInfos(new VpnInfo[0]);
+ mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]);
}
@After
@@ -105,8 +105,9 @@
@Test
public void testVpnRewriteTrafficThroughItself() throws Exception {
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -134,8 +135,9 @@
@Test
public void testVpnWithClat() throws Exception {
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] {
+ createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
mFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
@@ -167,8 +169,9 @@
@Test
public void testVpnWithOneUnderlyingIface() throws Exception {
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -191,8 +194,9 @@
@Test
public void testVpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -219,8 +223,9 @@
@Test
public void testVpnWithOneUnderlyingIface_withCompression() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -242,8 +247,9 @@
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is duplicating traffic across both WiFi and Cell.
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -267,10 +273,10 @@
public void testConcurrentVpns() throws Exception {
// Assume two VPNs are connected on two different network interfaces. VPN1 is using
// TEST_IFACE and VPN2 is using TEST_IFACE2.
- final VpnInfo[] vpnInfos = new VpnInfo[] {
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] {
createVpnInfo(TUN_IFACE, new String[] {TEST_IFACE}),
createVpnInfo(TUN_IFACE2, new String[] {TEST_IFACE2})};
- mFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -308,8 +314,9 @@
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -335,8 +342,9 @@
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface:
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
@@ -357,8 +365,9 @@
public void testVpnWithIncorrectUnderlyingIface() throws Exception {
// WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
// but has declared only WiFi (TEST_IFACE) in its underlying network set.
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index b4e37de..dde78aa 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -86,7 +86,7 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
-import android.net.VpnInfo;
+import android.net.UnderlyingNetworkInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -286,7 +286,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -328,7 +329,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -401,7 +403,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// modify some number on wifi, and trigger poll event
incrementCurrentTime(2 * HOUR_IN_MILLIS);
@@ -441,7 +444,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some traffic on first network
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -475,7 +479,8 @@
.insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
.insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
forcePollAndWaitForIdle();
@@ -514,7 +519,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -581,7 +587,7 @@
setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS);
mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
- new VpnInfo[0]);
+ new UnderlyingNetworkInfo[0]);
// Create some traffic.
incrementCurrentTime(MINUTE_IN_MILLIS);
@@ -655,7 +661,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some traffic for two apps
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -713,7 +720,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
NetworkStats.Entry entry1 = new NetworkStats.Entry(
TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
@@ -756,7 +764,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
NetworkStats.Entry uidStats = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
@@ -810,7 +819,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -867,7 +877,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -906,7 +917,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// Create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -943,7 +955,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some tethering traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -999,7 +1012,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -1104,7 +1118,8 @@
mService.registerNetworkStatsProvider("TEST", provider);
assertNotNull(cb);
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// Verifies that one requestStatsUpdate will be called during iface update.
provider.expectOnRequestStatsUpdate(0 /* unused */);
@@ -1155,7 +1170,8 @@
expectDefaultSettings();
NetworkState[] states =
new NetworkState[]{buildWifiState(true /* isMetered */, TEST_IFACE)};
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// Register custom provider and retrieve callback.
final TestableNetworkStatsProviderBinder provider =
@@ -1204,7 +1220,7 @@
// 3G network comes online.
setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS);
mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
- new VpnInfo[0]);
+ new UnderlyingNetworkInfo[0]);
// Create some traffic.
incrementCurrentTime(MINUTE_IN_MILLIS);
@@ -1274,7 +1290,8 @@
NetworkState[] states = new NetworkState[]{
buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)};
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// Create some traffic on mobile network.
incrementCurrentTime(HOUR_IN_MILLIS);
diff --git a/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java b/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java
new file mode 100644
index 0000000..5ab4dc6
--- /dev/null
+++ b/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java
@@ -0,0 +1,77 @@
+/*
+ * 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.fsverity;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.rules.ExternalResource;
+
+public final class AddFsVerityCertRule extends ExternalResource {
+
+ private static final String APK_VERITY_STANDARD_MODE = "2";
+
+ private final BaseHostJUnit4Test mHost;
+ private final String mCertPath;
+ private String mKeyId;
+
+ public AddFsVerityCertRule(BaseHostJUnit4Test host, String certPath) {
+ mHost = host;
+ mCertPath = certPath;
+ }
+
+ @Override
+ protected void before() throws Throwable {
+ ITestDevice device = mHost.getDevice();
+ String apkVerityMode = device.getProperty("ro.apk_verity.mode");
+ assumeTrue(device.getLaunchApiLevel() >= 30
+ || APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
+
+ String keyId = executeCommand(
+ "mini-keyctl padd asymmetric fsv_test .fs-verity < " + mCertPath).trim();
+ assertThat(keyId).matches("^\\d+$");
+ mKeyId = keyId;
+ }
+
+ @Override
+ protected void after() {
+ if (mKeyId == null) return;
+ try {
+ executeCommand("mini-keyctl unlink " + mKeyId + " .fs-verity");
+ } catch (DeviceNotAvailableException e) {
+ LogUtil.CLog.e(e);
+ }
+ mKeyId = null;
+ }
+
+ private String executeCommand(String cmd) throws DeviceNotAvailableException {
+ CommandResult result = mHost.getDevice().executeShellV2Command(cmd);
+ assertWithMessage("`" + cmd + "` failed: " + result.getStderr())
+ .that(result.getStatus())
+ .isEqualTo(CommandStatus.SUCCESS);
+ return result.getStdout();
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 9c6b719..f9db408 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -18,14 +18,19 @@
import static androidx.test.InstrumentationRegistry.getContext;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
import org.junit.Before;
@@ -103,4 +108,28 @@
public void testRemoveVcnUnderlyingNetworkPolicyListenerNullListener() {
mVcnManager.removeVcnUnderlyingNetworkPolicyListener(null);
}
+
+ @Test
+ public void testGetUnderlyingNetworkPolicy() throws Exception {
+ NetworkCapabilities nc = new NetworkCapabilities();
+ LinkProperties lp = new LinkProperties();
+ when(mMockVcnManagementService.getUnderlyingNetworkPolicy(eq(nc), eq(lp)))
+ .thenReturn(new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, nc));
+
+ VcnUnderlyingNetworkPolicy policy = mVcnManager.getUnderlyingNetworkPolicy(nc, lp);
+
+ assertFalse(policy.isTeardownRequested());
+ assertEquals(nc, policy.getMergedNetworkCapabilities());
+ verify(mMockVcnManagementService).getUnderlyingNetworkPolicy(eq(nc), eq(lp));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testGetUnderlyingNetworkPolicyNullNetworkCapabilities() throws Exception {
+ mVcnManager.getUnderlyingNetworkPolicy(null, new LinkProperties());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testGetUnderlyingNetworkPolicyNullLinkProperties() throws Exception {
+ mVcnManager.getUnderlyingNetworkPolicy(new NetworkCapabilities(), null);
+ }
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index f0cdde3..e7d334e 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -21,6 +21,7 @@
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -28,6 +29,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -37,13 +39,20 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkCapabilities.Transport;
+import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
+import android.net.vcn.VcnUnderlyingNetworkPolicy;
+import android.net.wifi.WifiInfo;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -63,6 +72,7 @@
import com.android.server.vcn.VcnNetworkProvider;
import com.android.server.vcn.util.PersistableBundleUtils;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -192,6 +202,14 @@
mTestLooper.dispatchAll();
}
+ @Before
+ public void setUp() {
+ doNothing()
+ .when(mMockContext)
+ .enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_FACTORY), any());
+ }
+
private void setupMockedCarrierPrivilege(boolean isPrivileged) {
doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
.when(mSubMgr)
@@ -239,7 +257,14 @@
verify(mConfigReadWriteHelper).readFromDisk();
}
- private void triggerSubscriptionTrackerCallback(Set<ParcelUuid> activeSubscriptionGroups) {
+ private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
+ Set<ParcelUuid> activeSubscriptionGroups) {
+ return triggerSubscriptionTrackerCbAndGetSnapshot(
+ activeSubscriptionGroups, Collections.emptyMap());
+ }
+
+ private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
+ Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap) {
final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
@@ -253,8 +278,14 @@
argThat(val -> activeSubscriptionGroups.contains(val)),
eq(TEST_PACKAGE_NAME));
+ doAnswer(invocation -> {
+ return subIdToGroupMap.get(invocation.getArgument(0));
+ }).when(snapshot).getGroupForSubId(anyInt());
+
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
cb.onNewSnapshot(snapshot);
+
+ return snapshot;
}
private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() {
@@ -273,7 +304,7 @@
@Test
public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
- triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_1));
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG));
}
@@ -282,7 +313,7 @@
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
- triggerSubscriptionTrackerCallback(Collections.emptySet());
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Verify teardown after delay
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
@@ -297,13 +328,13 @@
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
// Simulate SIM unloaded
- triggerSubscriptionTrackerCallback(Collections.emptySet());
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Simulate new SIM loaded right during teardown delay.
mTestLooper.moveTimeForward(
VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
mTestLooper.dispatchAll();
- triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_2));
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
// Verify that even after the full timeout duration, the VCN instance is not torn down
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
@@ -317,7 +348,7 @@
final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
// Simulate SIM unloaded
- triggerSubscriptionTrackerCallback(Collections.emptySet());
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
// vcnInstance.
@@ -455,10 +486,6 @@
@Test
public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception {
- doNothing()
- .when(mMockContext)
- .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
-
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
verify(mMockIBinder).linkToDeath(any(), anyInt());
@@ -468,17 +495,14 @@
public void testAddVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
doThrow(new SecurityException())
.when(mMockContext)
- .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+ .enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_FACTORY), any());
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
}
@Test
public void testRemoveVcnUnderlyingNetworkPolicyListener() {
- // verify listener added
- doNothing()
- .when(mMockContext)
- .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
@@ -488,4 +512,83 @@
public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
}
+
+ private void setUpVcnSubscription(int subId, ParcelUuid subGroup) {
+ mVcnMgmtSvc.setVcnConfig(subGroup, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
+ }
+
+ private void verifyMergedNetworkCapabilities(
+ NetworkCapabilities mergedCapabilities, @Transport int transportType) {
+ assertTrue(mergedCapabilities.hasTransport(transportType));
+ assertFalse(
+ mergedCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception {
+ setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
+
+ NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
+ .build();
+
+ VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+
+ assertFalse(policy.isTeardownRequested());
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception {
+ setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
+
+ WifiInfo wifiInfo = mock(WifiInfo.class);
+ when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
+ when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
+ NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setTransportInfo(wifiInfo)
+ .build();
+
+ VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+
+ assertFalse(policy.isTeardownRequested());
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception {
+ NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
+ .build();
+
+ VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+
+ assertFalse(policy.isTeardownRequested());
+ assertEquals(nc, policy.getMergedNetworkCapabilities());
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testGetUnderlyingNetworkPolicyInvalidPermission() {
+ doThrow(new SecurityException())
+ .when(mMockContext)
+ .enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(new NetworkCapabilities(), new LinkProperties());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
new file mode 100644
index 0000000..48e068d
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -0,0 +1,366 @@
+/*
+ * 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.vcn;
+
+import static com.android.server.vcn.VcnTestUtils.setupSystemService;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.TelephonyNetworkSpecifier;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
+import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+public class UnderlyingNetworkTrackerTest {
+ private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+ private static final int INITIAL_SUB_ID_1 = 1;
+ private static final int INITIAL_SUB_ID_2 = 2;
+
+ private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+ private static final NetworkCapabilities SUSPENDED_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder(INITIAL_NETWORK_CAPABILITIES)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .build();
+ private static final NetworkCapabilities UPDATED_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .build();
+
+ private static final LinkProperties INITIAL_LINK_PROPERTIES =
+ getLinkPropertiesWithName("initial_iface");
+ private static final LinkProperties UPDATED_LINK_PROPERTIES =
+ getLinkPropertiesWithName("updated_iface");
+
+ @Mock private Context mContext;
+ @Mock private VcnNetworkProvider mVcnNetworkProvider;
+ @Mock private ConnectivityManager mConnectivityManager;
+ @Mock private SubscriptionManager mSubscriptionManager;
+ @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
+ @Mock private Network mNetwork;
+
+ @Captor private ArgumentCaptor<RouteSelectionCallback> mRouteSelectionCallbackCaptor;
+
+ private TestLooper mTestLooper;
+ private VcnContext mVcnContext;
+ private UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTestLooper = new TestLooper();
+ mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider));
+ doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+
+ setupSystemService(
+ mContext,
+ mConnectivityManager,
+ Context.CONNECTIVITY_SERVICE,
+ ConnectivityManager.class);
+ setupSystemService(
+ mContext,
+ mSubscriptionManager,
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE,
+ SubscriptionManager.class);
+
+ List<SubscriptionInfo> initialSubInfos =
+ Arrays.asList(
+ getSubscriptionInfoForSubId(INITIAL_SUB_ID_1),
+ getSubscriptionInfoForSubId(INITIAL_SUB_ID_2));
+ when(mSubscriptionManager.getSubscriptionsInGroup(eq(SUB_GROUP)))
+ .thenReturn(initialSubInfos);
+
+ mUnderlyingNetworkTracker =
+ new UnderlyingNetworkTracker(
+ mVcnContext,
+ SUB_GROUP,
+ Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
+ mNetworkTrackerCb);
+ }
+
+ private static LinkProperties getLinkPropertiesWithName(String iface) {
+ LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(iface);
+ return linkProperties;
+ }
+
+ private SubscriptionInfo getSubscriptionInfoForSubId(int subId) {
+ SubscriptionInfo subInfo = mock(SubscriptionInfo.class);
+ when(subInfo.getSubscriptionId()).thenReturn(subId);
+ return subInfo;
+ }
+
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartup() {
+ // verify NetworkCallbacks registered when instantiated
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getWifiRequest()),
+ any(),
+ any(NetworkBringupCallback.class));
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getCellRequestForSubId(INITIAL_SUB_ID_1)),
+ any(),
+ any(NetworkBringupCallback.class));
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getCellRequestForSubId(INITIAL_SUB_ID_2)),
+ any(),
+ any(NetworkBringupCallback.class));
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getRouteSelectionRequest()),
+ any(),
+ any(RouteSelectionCallback.class));
+
+ verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP));
+ }
+
+ private NetworkRequest getWifiRequest() {
+ return getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .build();
+ }
+
+ private NetworkRequest getCellRequestForSubId(int subId) {
+ return getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
+ .build();
+ }
+
+ private NetworkRequest getRouteSelectionRequest() {
+ return getExpectedRequestBase().build();
+ }
+
+ private NetworkRequest.Builder getExpectedRequestBase() {
+ return new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ @Test
+ public void testTeardown() {
+ mUnderlyingNetworkTracker.teardown();
+
+ // Expect 3 NetworkBringupCallbacks to be unregistered: 1 for WiFi and 2 for Cellular (1x
+ // for each subId)
+ verify(mConnectivityManager, times(3))
+ .unregisterNetworkCallback(any(NetworkBringupCallback.class));
+ verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
+ }
+
+ @Test
+ public void testUnderlyingNetworkRecordEquals() {
+ UnderlyingNetworkRecord recordA =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ UnderlyingNetworkRecord recordB =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ UnderlyingNetworkRecord recordC =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ UPDATED_NETWORK_CAPABILITIES,
+ UPDATED_LINK_PROPERTIES,
+ false /* isBlocked */);
+
+ assertEquals(recordA, recordB);
+ assertNotEquals(recordA, recordC);
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkChange() {
+ verifyRegistrationOnAvailableAndGetCallback();
+ }
+
+ private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback() {
+ return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
+ }
+
+ private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback(
+ NetworkCapabilities networkCapabilities) {
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getRouteSelectionRequest()),
+ any(),
+ mRouteSelectionCallbackCaptor.capture());
+
+ RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue();
+ cb.onAvailable(mNetwork);
+ cb.onCapabilitiesChanged(mNetwork, networkCapabilities);
+ cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES);
+ cb.onBlockedStatusChanged(mNetwork, false /* isFalse */);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ networkCapabilities,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ return cb;
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ UPDATED_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForLinkPropertiesChange() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ UPDATED_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkSuspended() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onNetworkSuspended(mNetwork);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ SUSPENDED_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkResumed() {
+ RouteSelectionCallback cb =
+ verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES);
+
+ cb.onNetworkResumed(mNetwork);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForBlocked() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ true /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkLoss() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onLost(mNetwork);
+
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(null);
+ }
+
+ @Test
+ public void testRecordTrackerCallbackIgnoresDuplicateRecord() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
+
+ // Verify no more calls to the UnderlyingNetworkTrackerCallback when the
+ // UnderlyingNetworkRecord does not actually change
+ verifyNoMoreInteractions(mNetworkTrackerCb);
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
new file mode 100644
index 0000000..e20070e
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.vcn;
+
+import static android.net.IpSecManager.DIRECTION_IN;
+import static android.net.IpSecManager.DIRECTION_OUT;
+
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.ConnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
+ private VcnIkeSession mIkeSession;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+
+ mIkeSession = mGatewayConnection.buildIkeSession();
+ mGatewayConnection.setIkeSession(mIkeSession);
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mConnectedState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testEnterStateCreatesNewIkeSession() throws Exception {
+ verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession, never()).close();
+ }
+
+ @Test
+ public void testNewNetworkTriggersMigration() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession, never()).close();
+ verify(mIkeSession).setNetwork(TEST_UNDERLYING_NETWORK_RECORD_2.network);
+ }
+
+ @Test
+ public void testSameNetworkDoesNotTriggerMigration() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testCreatedTransformsAreApplied() throws Exception {
+ for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
+ getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+ mTestLooper.dispatchAll();
+
+ verify(mIpSecSvc)
+ .applyTunnelModeTransform(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
+ }
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testChildSessionClosedTriggersDisconnect() throws Exception {
+ getChildSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testIkeSessionClosedTriggersDisconnect() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+
+ // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index d741e5c..f4ac86d 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -16,20 +16,35 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import android.annotation.NonNull;
import android.content.Context;
+import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
import android.os.ParcelUuid;
+import android.os.Process;
import android.os.test.TestLooper;
import android.telephony.SubscriptionInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,6 +57,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionTest {
+ private static final int TEST_UID = Process.myUid();
private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
private static final int TEST_SIM_SLOT_INDEX = 1;
private static final int TEST_SUBSCRIPTION_ID_1 = 2;
@@ -61,22 +77,59 @@
@NonNull private final TestLooper mTestLooper;
@NonNull private final VcnNetworkProvider mVcnNetworkProvider;
@NonNull private final VcnGatewayConnection.Dependencies mDeps;
+ @NonNull private final WifiInfo mWifiInfo;
public VcnGatewayConnectionTest() {
mContext = mock(Context.class);
mTestLooper = new TestLooper();
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
+ mWifiInfo = mock(WifiInfo.class);
+ }
+
+ private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
+ final NetworkCapabilities underlyingCaps = new NetworkCapabilities();
+ underlyingCaps.addTransportType(transportType);
+ underlyingCaps.addCapability(NET_CAPABILITY_NOT_METERED);
+ underlyingCaps.addCapability(NET_CAPABILITY_NOT_ROAMING);
+
+ if (transportType == TRANSPORT_WIFI) {
+ underlyingCaps.setTransportInfo(mWifiInfo);
+ underlyingCaps.setOwnerUid(TEST_UID);
+ } else if (transportType == TRANSPORT_CELLULAR) {
+ underlyingCaps.setAdministratorUids(new int[] {TEST_UID});
+ underlyingCaps.setNetworkSpecifier(
+ new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_1));
+ }
+
+ UnderlyingNetworkRecord record =
+ new UnderlyingNetworkRecord(
+ new Network(0), underlyingCaps, new LinkProperties(), false);
+ final NetworkCapabilities vcnCaps =
+ VcnGatewayConnection.buildNetworkCapabilities(
+ VcnGatewayConnectionConfigTest.buildTestConfig(), record);
+
+ assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids());
+ assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo);
+
+ final VcnTransportInfo info = (VcnTransportInfo) vcnCaps.getTransportInfo();
+ if (transportType == TRANSPORT_WIFI) {
+ assertEquals(mWifiInfo, info.getWifiInfo());
+ } else if (transportType == TRANSPORT_CELLULAR) {
+ assertEquals(TEST_SUBSCRIPTION_ID_1, info.getSubId());
+ }
}
@Test
- public void testBuildNetworkCapabilities() throws Exception {
- final NetworkCapabilities caps =
- VcnGatewayConnection.buildNetworkCapabilities(
- VcnGatewayConnectionConfigTest.buildTestConfig());
+ public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception {
+ verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI);
+ }
- for (int exposedCapability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
- assertTrue(caps.hasCapability(exposedCapability));
- }
+ @Test
+ public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
+ verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index b4d39bf..8730780 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -27,7 +27,9 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.net.IpSecConfig;
import android.net.IpSecManager;
+import android.net.IpSecTransform;
import android.net.IpSecTunnelInterfaceResponse;
import android.net.LinkProperties;
import android.net.Network;
@@ -48,7 +50,10 @@
public class VcnGatewayConnectionTestBase {
protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
- protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1;
+ protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
+ protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
+ protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
+ protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 3;
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
new UnderlyingNetworkRecord(
@@ -94,7 +99,7 @@
doReturn(mUnderlyingNetworkTracker)
.when(mDeps)
- .newUnderlyingNetworkTracker(any(), any(), any());
+ .newUnderlyingNetworkTracker(any(), any(), any(), any());
}
@Before
@@ -112,6 +117,10 @@
mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
}
+ protected IpSecTransform makeDummyIpSecTransform() throws Exception {
+ return new IpSecTransform(mContext, new IpSecConfig());
+ }
+
protected IkeSessionCallback getIkeSessionCallback() {
ArgumentCaptor<IkeSessionCallback> captor =
ArgumentCaptor.forClass(IkeSessionCallback.class);
diff --git a/tools/fonts/update_font_metadata.py b/tools/fonts/update_font_metadata.py
new file mode 100755
index 0000000..c07a98a
--- /dev/null
+++ b/tools/fonts/update_font_metadata.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+
+import argparse
+
+from fontTools import ttLib
+
+
+def update_font_revision(font, revisionSpec):
+ if revisionSpec.startswith('+'):
+ font['head'].fontRevision += float(revisionSpec[1:])
+ else:
+ font['head'].fontRevision = float(revisionSpec)
+
+
+def main():
+ args_parser = argparse.ArgumentParser(description='Update font file metadata')
+ args_parser.add_argument('--input', help='Input otf/ttf font file.')
+ args_parser.add_argument('--output', help='Output file for updated font file.')
+ args_parser.add_argument('--revision', help='Updated font revision. Use + to update revision based on the current revision')
+ args = args_parser.parse_args()
+
+ font = ttLib.TTFont(args.input)
+ update_font_revision(font, args.revision)
+ font.save(args.output)
+
+if __name__ == "__main__":
+ main()
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index ef26532..c64f4bc 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.AlarmManager;
@@ -948,8 +949,9 @@
* has been set up).
*/
public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
- @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs,
- @Nullable Bundle extraScanningParams) {
+ @SuppressLint("NullableCollection") @Nullable Set<Integer> freqs,
+ @SuppressLint("NullableCollection") @Nullable List<byte[]> hiddenNetworkSSIDs,
+ @SuppressLint("NullableCollection") @Nullable Bundle extraScanningParams) {
IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
if (scannerImpl == null) {
Log.e(TAG, "No valid wificond scanner interface handler for iface=" + ifaceName);