Merge "Update Layout.Builder APIs" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 16907b3..b753aab 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -19,6 +19,7 @@
// Add java_aconfig_libraries to here to add them to the core framework
srcs: [
":com.android.hardware.camera2-aconfig-java{.generated_srcjars}",
+ ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
],
}
@@ -30,6 +31,7 @@
libs: ["fake_device_config"],
}
+// Camera
aconfig_declarations {
name: "com.android.hardware.camera2-aconfig",
package: "com.android.hardware.camera2",
@@ -41,3 +43,16 @@
aconfig_declarations: "com.android.hardware.camera2-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Window
+aconfig_declarations {
+ name: "com.android.window.flags.window-aconfig",
+ package: "com.android.window.flags",
+ srcs: ["core/java/android/window/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.window.flags.window-aconfig-java",
+ aconfig_declarations: "com.android.window.flags.window-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 53554bc..431f0b9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -408,21 +408,18 @@
],
}
-java_library {
- name: "framework-minus-apex",
+// Separated so framework-minus-apex-defaults can be used without the libs dependency
+java_defaults {
+ name: "framework-minus-apex-with-libs-defaults",
defaults: ["framework-minus-apex-defaults"],
- installable: true,
- // For backwards compatibility.
- stem: "framework",
- apex_available: ["//apex_available:platform"],
- visibility: [
- "//frameworks/base",
- // TODO(b/147128803) remove the below lines
- "//frameworks/base/apex/blobstore/framework",
- "//frameworks/base/apex/jobscheduler/framework",
- "//frameworks/base/packages/Tethering/tests/unit",
- "//packages/modules/Connectivity/Tethering/tests/unit",
+ libs: [
+ "framework-virtualization.stubs.module_lib",
+ "framework-location.impl",
],
+}
+
+java_defaults {
+ name: "framework-non-updatable-lint-defaults",
lint: {
extra_check_modules: ["AndroidFrameworkLintChecker"],
disabled_checks: ["ApiMightLeakAppVisibility"],
@@ -436,6 +433,43 @@
"UseOfCallerAwareMethodsWithClearedIdentity",
],
},
+}
+
+// we are unfortunately building the turbine jar twice, but more efficient and less complex
+// than generating a similar set of stubs with metalava
+java_library {
+ name: "framework-minus-apex-headers",
+ defaults: ["framework-minus-apex-defaults"],
+ installable: false,
+ // For backwards compatibility.
+ stem: "framework",
+ apex_available: ["//apex_available:platform"],
+ visibility: [
+ "//frameworks/base/location",
+ ],
+ compile_dex: false,
+ headers_only: true,
+}
+
+java_library {
+ name: "framework-minus-apex",
+ defaults: [
+ "framework-minus-apex-with-libs-defaults",
+ "framework-non-updatable-lint-defaults",
+ ],
+ installable: true,
+ // For backwards compatibility.
+ stem: "framework",
+ apex_available: ["//apex_available:platform"],
+ visibility: [
+ "//frameworks/base",
+ "//frameworks/base/location",
+ // TODO(b/147128803) remove the below lines
+ "//frameworks/base/apex/blobstore/framework",
+ "//frameworks/base/apex/jobscheduler/framework",
+ "//frameworks/base/packages/Tethering/tests/unit",
+ "//packages/modules/Connectivity/Tethering/tests/unit",
+ ],
errorprone: {
javacflags: [
"-Xep:AndroidFrameworkCompatChange:ERROR",
@@ -446,7 +480,7 @@
java_library {
name: "framework-minus-apex-intdefs",
- defaults: ["framework-minus-apex-defaults"],
+ defaults: ["framework-minus-apex-with-libs-defaults"],
plugins: ["intdef-annotation-processor"],
// Errorprone and android lint will already run on framework-minus-apex, don't rerun them on
@@ -474,6 +508,7 @@
installable: false, // this lib is a build-only library
static_libs: [
"app-compat-annotations",
+ "framework-location.impl",
"framework-minus-apex",
"framework-updatable-stubs-module_libs_api",
],
@@ -701,6 +736,97 @@
],
}
+// Defaults for the java_sdk_libraries of unbundled jars from framework.
+// java_sdk_libraries using these defaults should also add themselves to the
+// non_updatable_modules list in frameworks/base/api/api.go
+java_defaults {
+ name: "framework-non-updatable-unbundled-defaults",
+ defaults: ["framework-non-updatable-lint-defaults"],
+
+ sdk_version: "core_platform",
+
+ // Api scope settings
+ public: {
+ enabled: true,
+ sdk_version: "module_current",
+ libs: ["android_module_lib_stubs_current"],
+ },
+ system: {
+ enabled: true,
+ sdk_version: "module_current",
+ libs: ["android_module_lib_stubs_current"],
+ },
+ module_lib: {
+ enabled: true,
+ sdk_version: "module_current",
+ libs: ["android_module_lib_stubs_current"],
+ },
+ test: {
+ enabled: true,
+ sdk_version: "test_frameworks_core_current",
+ libs: ["android_test_frameworks_core_stubs_current"],
+ },
+
+ stub_only_libs: [
+ "framework-protos",
+ ],
+ impl_only_libs: [
+ "framework-minus-apex-headers", // full access to framework-minus-apex including hidden API
+ "framework-annotations-lib",
+ ],
+ visibility: ["//visibility:public"],
+ stubs_library_visibility: ["//visibility:public"],
+ stubs_source_visibility: ["//visibility:private"],
+ impl_library_visibility: [
+ ":__pkg__",
+ "//frameworks/base",
+ "//frameworks/base/api", // For framework-all
+ ],
+ defaults_visibility: [
+ "//frameworks/base/location",
+ ],
+ plugins: [
+ "error_prone_android_framework",
+ ],
+ errorprone: {
+ javacflags: [
+ "-Xep:AndroidFrameworkCompatChange:ERROR",
+ "-Xep:AndroidFrameworkUid:ERROR",
+ ],
+ },
+
+ // Include manual annotations in API txt files
+ merge_annotations_dirs: ["metalava-manual"],
+
+ // Use the source of annotations that affect metalava doc generation, since
+ // the relevant generation instructions are themselves in javadoc, which is
+ // not present in class files.
+ api_srcs: [":framework-metalava-annotations"],
+
+ // 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,
+
+ // Subdirectory for the artifacts that are copied to the dist directory
+ dist_group: "android",
+
+ droiddoc_options: [
+ "--error UnhiddenSystemApi " +
+ "--hide CallbackInterface " +
+ "--hide HiddenTypedefConstant " +
+ "--hide RequiresPermission " +
+ "--enhance-documentation " +
+ "--hide-package com.android.server ",
+ ],
+
+ annotations_enabled: true,
+}
+
build = [
"AconfigFlags.bp",
"ProtoLibraries.bp",
diff --git a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
index 9482591..aadbc23 100644
--- a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
+++ b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
@@ -130,7 +130,7 @@
eventTime, ACTION_MOVE, /*x=*/eventPosition, /*y=*/eventPosition)
predictor.record(moveEvent)
val predictionTime = eventTime + eventInterval
- val predicted = predictor.predict(predictionTime.toNanos())
+ val predicted = checkNotNull(predictor.predict(predictionTime.toNanos()))
assertTrue(predicted.eventTime <= (predictionTime + offset).toMillis())
}
}
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 109686d..e636f60 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -456,6 +456,7 @@
if (DEBUG) {
Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
}
+ mContext.unbindService(this);
mRunningJob = null;
mRunningJobWorkType = WORK_TYPE_NONE;
mRunningCallback = null;
diff --git a/api/Android.bp b/api/Android.bp
index e9cc405..6986ac0 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -87,6 +87,7 @@
"framework-devicelock",
"framework-graphics",
"framework-healthfitness",
+ "framework-location",
"framework-media",
"framework-mediaprovider",
"framework-ondevicepersonalization",
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index c37ff97..e481fe9 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -487,7 +487,6 @@
static_libs: [
"all-updatable-modules-system-stubs",
"android-non-updatable.stubs.test",
- "private-stub-annotations-jar",
],
defaults: [
"android.jar_defaults",
@@ -613,6 +612,7 @@
api_contributions: [
"test-api-stubs-docs-non-updatable.api.contribution",
"framework-virtualization.stubs.source.test.api.contribution",
+ "framework-location.stubs.source.test.api.contribution",
],
}
diff --git a/api/api.go b/api/api.go
index d5c6145..6095a9a 100644
--- a/api/api.go
+++ b/api/api.go
@@ -31,6 +31,7 @@
const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
const virtualization = "framework-virtualization"
+const location = "framework-location"
var core_libraries_modules = []string{art, conscrypt, i18n}
@@ -42,7 +43,7 @@
// APIs.
// In addition, the modules in this list are allowed to contribute to test APIs
// stubs.
-var non_updatable_modules = []string{virtualization}
+var non_updatable_modules = []string{virtualization, location}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
@@ -278,8 +279,10 @@
}
func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
- // The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
+ // The user of this module compiles against the "core" SDK and against non-updatable modules,
+ // so remove to avoid dupes.
modules = removeAll(modules, core_libraries_modules)
+ modules = removeAll(modules, non_updatable_modules)
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 3534624..98767ee 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -74,4 +74,7 @@
"libGLESv2",
"libgui",
],
+ whole_static_libs: [
+ "libc++fs",
+ ],
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 774ba74..b56dceb 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "BootAnimation"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <filesystem>
#include <vector>
#include <stdint.h>
@@ -1306,10 +1307,10 @@
continue;
}
- const String8 entryName(name);
- const String8 path(entryName.getPathDir());
- const String8 leaf(entryName.getPathLeaf());
- if (leaf.size() > 0) {
+ const std::filesystem::path entryName(name);
+ const std::filesystem::path path(entryName.parent_path());
+ const std::filesystem::path leaf(entryName.filename());
+ if (!leaf.empty()) {
if (entryName == CLOCK_FONT_ZIP_NAME) {
FileMap* map = zip->createEntryFileMap(entry);
if (map) {
@@ -1327,7 +1328,7 @@
}
for (size_t j = 0; j < pcount; j++) {
- if (path == animation.parts[j].path) {
+ if (path.string() == animation.parts[j].path.c_str()) {
uint16_t method;
// supports only stored png files
if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, nullptr)) {
@@ -1344,7 +1345,7 @@
map->getDataLength());
} else {
Animation::Frame frame;
- frame.name = leaf;
+ frame.name = leaf.c_str();
frame.map = map;
frame.trimWidth = animation.width;
frame.trimHeight = animation.height;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6141583..2b3e8e9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -301,6 +301,7 @@
field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
+ field public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
@@ -17233,9 +17234,8 @@
method public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
}
- public class SatelliteManager {
+ public final class SatelliteManager {
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void onDeviceAlignedWithSatellite(boolean);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
@@ -17250,6 +17250,7 @@
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendSatelliteDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3927b40..3abe1d9 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -24,6 +24,7 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UiThread;
import android.annotation.UserIdInt;
import android.app.IServiceConnection;
import android.app.PendingIntent;
@@ -39,6 +40,10 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.DisplayMetrics;
@@ -53,6 +58,7 @@
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
/**
* Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -475,6 +481,8 @@
private static final String TAG = "AppWidgetManager";
+ private static Executor sUpdateExecutor;
+
/**
* An intent extra that contains multiple appWidgetIds. These are id values as
* they were provided to the application during a recent restore from backup. It is
@@ -510,6 +518,8 @@
private final IAppWidgetService mService;
private final DisplayMetrics mDisplayMetrics;
+ private boolean mHasPostedLegacyLists = false;
+
/**
* Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
* Context} object.
@@ -552,6 +562,13 @@
});
}
+ private boolean isPostingTaskToBackground(@Nullable RemoteViews views) {
+ return Looper.myLooper() == Looper.getMainLooper()
+ && RemoteViews.isAdapterConversionEnabled()
+ && (mHasPostedLegacyLists = mHasPostedLegacyLists
+ || (views != null && views.hasLegacyLists()));
+ }
+
/**
* Set the RemoteViews to use for the specified appWidgetIds.
* <p>
@@ -575,6 +592,19 @@
if (mService == null) {
return;
}
+
+ if (isPostingTaskToBackground(views)) {
+ createUpdateExecutorIfNull().execute(() -> {
+ try {
+ mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error updating app widget views in background", e);
+ }
+ });
+
+ return;
+ }
+
try {
mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
} catch (RemoteException e) {
@@ -683,6 +713,19 @@
if (mService == null) {
return;
}
+
+ if (isPostingTaskToBackground(views)) {
+ createUpdateExecutorIfNull().execute(() -> {
+ try {
+ mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error partially updating app widget views in background", e);
+ }
+ });
+
+ return;
+ }
+
try {
mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
} catch (RemoteException e) {
@@ -738,6 +781,19 @@
if (mService == null) {
return;
}
+
+ if (isPostingTaskToBackground(views)) {
+ createUpdateExecutorIfNull().execute(() -> {
+ try {
+ mService.updateAppWidgetProvider(provider, views);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error updating app widget view using provider in background", e);
+ }
+ });
+
+ return;
+ }
+
try {
mService.updateAppWidgetProvider(provider, views);
} catch (RemoteException e) {
@@ -797,28 +853,45 @@
if (mService == null) {
return;
}
- try {
- if (RemoteViews.isAdapterConversionEnabled()) {
- List<CompletableFuture<Void>> updateFutures = new ArrayList<>();
- for (int i = 0; i < appWidgetIds.length; i++) {
- final int widgetId = appWidgetIds[i];
- updateFutures.add(CompletableFuture.runAsync(() -> {
- try {
- RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId);
- if (views.replaceRemoteCollections(viewId)) {
- updateAppWidget(widgetId, views);
- }
- } catch (Exception e) {
- Log.e(TAG, "Error notifying changes in RemoteViews", e);
- }
- }));
- }
- CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join();
- } else {
+
+ if (!RemoteViews.isAdapterConversionEnabled()) {
+ try {
mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
}
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+
+ return;
+ }
+
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ mHasPostedLegacyLists = true;
+ createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange(appWidgetIds,
+ viewId));
+ } else {
+ notifyCollectionWidgetChange(appWidgetIds, viewId);
+ }
+ }
+
+ private void notifyCollectionWidgetChange(int[] appWidgetIds, int viewId) {
+ try {
+ List<CompletableFuture<Void>> updateFutures = new ArrayList<>();
+ for (int i = 0; i < appWidgetIds.length; i++) {
+ final int widgetId = appWidgetIds[i];
+ updateFutures.add(CompletableFuture.runAsync(() -> {
+ try {
+ RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId);
+ if (views.replaceRemoteCollections(viewId)) {
+ updateAppWidget(widgetId, views);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error notifying changes in RemoteViews", e);
+ }
+ }));
+ }
+ CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join();
+ } catch (Exception e) {
+ Log.e(TAG, "Error notifying changes for all widgets", e);
}
}
@@ -1360,4 +1433,20 @@
throw e.rethrowFromSystemServer();
}
}
+
+ @UiThread
+ private static @NonNull Executor createUpdateExecutorIfNull() {
+ if (sUpdateExecutor == null) {
+ sUpdateExecutor = new HandlerExecutor(createAndStartNewHandler(
+ "widget_manager_update_helper_thread", Process.THREAD_PRIORITY_FOREGROUND));
+ }
+
+ return sUpdateExecutor;
+ }
+
+ private static @NonNull Handler createAndStartNewHandler(@NonNull String name, int priority) {
+ HandlerThread thread = new HandlerThread(name, priority);
+ thread.start();
+ return thread.getThreadHandler();
+ }
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index ccc39b6..0396443 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -41,11 +41,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RSIllegalArgumentException;
-import android.renderscript.RenderScript;
-import android.renderscript.Type;
import android.text.TextUtils;
import android.util.Log;
import android.view.Surface;
@@ -1007,132 +1002,6 @@
private native final void _addCallbackBuffer(
byte[] callbackBuffer, int msgType);
- /**
- * <p>Create a {@link android.renderscript RenderScript}
- * {@link android.renderscript.Allocation Allocation} to use as a
- * destination of preview callback frames. Use
- * {@link #setPreviewCallbackAllocation setPreviewCallbackAllocation} to use
- * the created Allocation as a destination for camera preview frames.</p>
- *
- * <p>The Allocation will be created with a YUV type, and its contents must
- * be accessed within Renderscript with the {@code rsGetElementAtYuv_*}
- * accessor methods. Its size will be based on the current
- * {@link Parameters#getPreviewSize preview size} configured for this
- * camera.</p>
- *
- * @param rs the RenderScript context for this Allocation.
- * @param usage additional usage flags to set for the Allocation. The usage
- * flag {@link android.renderscript.Allocation#USAGE_IO_INPUT} will always
- * be set on the created Allocation, but additional flags may be provided
- * here.
- * @return a new YUV-type Allocation with dimensions equal to the current
- * preview size.
- * @throws RSIllegalArgumentException if the usage flags are not compatible
- * with an YUV Allocation.
- * @see #setPreviewCallbackAllocation
- * @hide
- */
- public final Allocation createPreviewAllocation(RenderScript rs, int usage)
- throws RSIllegalArgumentException {
- Parameters p = getParameters();
- Size previewSize = p.getPreviewSize();
- Type.Builder yuvBuilder = new Type.Builder(rs,
- Element.createPixel(rs,
- Element.DataType.UNSIGNED_8,
- Element.DataKind.PIXEL_YUV));
- // Use YV12 for wide compatibility. Changing this requires also
- // adjusting camera service's format selection.
- yuvBuilder.setYuvFormat(ImageFormat.YV12);
- yuvBuilder.setX(previewSize.width);
- yuvBuilder.setY(previewSize.height);
-
- Allocation a = Allocation.createTyped(rs, yuvBuilder.create(),
- usage | Allocation.USAGE_IO_INPUT);
-
- return a;
- }
-
- /**
- * <p>Set an {@link android.renderscript.Allocation Allocation} as the
- * target of preview callback data. Use this method for efficient processing
- * of camera preview data with RenderScript. The Allocation must be created
- * with the {@link #createPreviewAllocation createPreviewAllocation }
- * method.</p>
- *
- * <p>Setting a preview allocation will disable any active preview callbacks
- * set by {@link #setPreviewCallback setPreviewCallback} or
- * {@link #setPreviewCallbackWithBuffer setPreviewCallbackWithBuffer}, and
- * vice versa. Using a preview allocation still requires an active standard
- * preview target to be set, either with
- * {@link #setPreviewTexture setPreviewTexture} or
- * {@link #setPreviewDisplay setPreviewDisplay}.</p>
- *
- * <p>To be notified when new frames are available to the Allocation, use
- * {@link android.renderscript.Allocation#setIoInputNotificationHandler Allocation.setIoInputNotificationHandler}. To
- * update the frame currently accessible from the Allocation to the latest
- * preview frame, call
- * {@link android.renderscript.Allocation#ioReceive Allocation.ioReceive}.</p>
- *
- * <p>To disable preview into the Allocation, call this method with a
- * {@code null} parameter.</p>
- *
- * <p>Once a preview allocation is set, the preview size set by
- * {@link Parameters#setPreviewSize setPreviewSize} cannot be changed. If
- * you wish to change the preview size, first remove the preview allocation
- * by calling {@code setPreviewCallbackAllocation(null)}, then change the
- * preview size, create a new preview Allocation with
- * {@link #createPreviewAllocation createPreviewAllocation}, and set it as
- * the new preview callback allocation target.</p>
- *
- * <p>If you are using the preview data to create video or still images,
- * strongly consider using {@link android.media.MediaActionSound} to
- * properly indicate image capture or recording start/stop to the user.</p>
- *
- * @param previewAllocation the allocation to use as destination for preview
- * @throws IOException if configuring the camera to use the Allocation for
- * preview fails.
- * @throws IllegalArgumentException if the Allocation's dimensions or other
- * parameters don't meet the requirements.
- * @see #createPreviewAllocation
- * @see #setPreviewCallback
- * @see #setPreviewCallbackWithBuffer
- * @hide
- */
- public final void setPreviewCallbackAllocation(Allocation previewAllocation)
- throws IOException {
- Surface previewSurface = null;
- if (previewAllocation != null) {
- Parameters p = getParameters();
- Size previewSize = p.getPreviewSize();
- if (previewSize.width != previewAllocation.getType().getX() ||
- previewSize.height != previewAllocation.getType().getY()) {
- throw new IllegalArgumentException(
- "Allocation dimensions don't match preview dimensions: " +
- "Allocation is " +
- previewAllocation.getType().getX() +
- ", " +
- previewAllocation.getType().getY() +
- ". Preview is " + previewSize.width + ", " +
- previewSize.height);
- }
- if ((previewAllocation.getUsage() &
- Allocation.USAGE_IO_INPUT) == 0) {
- throw new IllegalArgumentException(
- "Allocation usage does not include USAGE_IO_INPUT");
- }
- if (previewAllocation.getType().getElement().getDataKind() !=
- Element.DataKind.PIXEL_YUV) {
- throw new IllegalArgumentException(
- "Allocation is not of a YUV type");
- }
- previewSurface = previewAllocation.getSurface();
- mUsingPreviewAllocation = true;
- } else {
- mUsingPreviewAllocation = false;
- }
- setPreviewCallbackSurface(previewSurface);
- }
-
private native final void setPreviewCallbackSurface(Surface s);
private class EventHandler extends Handler
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 0e45787..082a336 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -541,14 +541,6 @@
* or configuring it to use one of the supported
* {@link android.media.CamcorderProfile CamcorderProfiles}.</li>
*
- * <li>For efficient YUV processing with {@link android.renderscript}:
- * Create a RenderScript
- * {@link android.renderscript.Allocation Allocation} with a supported YUV
- * type, the IO_INPUT flag, and one of the sizes returned by
- * {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(Allocation.class)},
- * Then obtain the Surface with
- * {@link android.renderscript.Allocation#getSurface}.</li>
- *
* <li>For access to RAW, uncompressed YUV, or compressed JPEG data in the application: Create an
* {@link android.media.ImageReader} object with one of the supported output formats given by
* {@link StreamConfigurationMap#getOutputFormats()}, setting its size to one of the
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index ef0db7f..b85d686 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -509,8 +509,6 @@
* Recommended for recording video (simple to use)
* <li>{@link android.media.MediaCodec} -
* Recommended for recording video (more complicated to use, with more flexibility)
- * <li>{@link android.renderscript.Allocation} -
- * Recommended for image processing with {@link android.renderscript RenderScript}
* <li>{@link android.view.SurfaceHolder} -
* Recommended for low-power camera preview with {@link android.view.SurfaceView}
* <li>{@link android.graphics.SurfaceTexture} -
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index af09a06..5b24dca 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1294,32 +1294,30 @@
* Round the given size of a storage device to a nice round power-of-two
* value, such as 256MB or 32GB. This avoids showing weird values like
* "29.5GB" in UI.
- *
- * Some storage devices are still using GiB (powers of 1024) over
- * GB (powers of 1000) measurements and this method takes it into account.
- *
* Round ranges:
* ...
- * [256 GiB + 1; 512 GiB] -> 512 GB
- * [512 GiB + 1; 1 TiB] -> 1 TB
- * [1 TiB + 1; 2 TiB] -> 2 TB
+ * (128 GB; 256 GB] -> 256 GB
+ * (256 GB; 512 GB] -> 512 GB
+ * (512 GB; 1000 GB] -> 1000 GB
+ * (1000 GB; 2000 GB] -> 2000 GB
+ * ...
* etc
*
* @hide
*/
public static long roundStorageSize(long size) {
long val = 1;
- long kiloPow = 1;
- long kibiPow = 1;
- while ((val * kibiPow) < size) {
+ long pow = 1;
+ while ((val * pow) < size) {
val <<= 1;
if (val > 512) {
val = 1;
- kibiPow *= 1024;
- kiloPow *= 1000;
+ pow *= 1000;
}
}
- return val * kiloPow;
+
+ Log.d(TAG, String.format("Rounded bytes from %d to %d", size, val * pow));
+ return val * pow;
}
private static long toBytes(long value, String unit) {
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index bc52744..369a193 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -174,4 +174,5 @@
boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 95;
void setCloudMediaProvider(in String authority) = 96;
String getCloudMediaProvider() = 97;
+ long getInternalStorageBlockDeviceSize() = 98;
}
\ No newline at end of file
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 80dd488..ee387e7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1359,6 +1359,15 @@
}
/** {@hide} */
+ public long getInternalStorageBlockDeviceSize() {
+ try {
+ return mStorageManager.getInternalStorageBlockDeviceSize();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** {@hide} */
public void mkdirs(File file) {
BlockGuard.getVmPolicy().onPathAccess(file.getAbsolutePath());
try {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2791c41..b9a6583 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4579,6 +4579,16 @@
public static final String WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE =
"wear_accessibility_gesture_enabled_during_oobe";
+
+ /**
+ * If the text-to-speech pre-warm is enabled.
+ * Set to 1 for true and 0 for false.
+ *
+ * This setting is used only internally.
+ * @hide
+ */
+ public static final String WEAR_TTS_PREWARM_ENABLED = "wear_tts_prewarm_enabled";
+
/**
* @deprecated Use {@link android.provider.Settings.Global#AIRPLANE_MODE_ON} instead
*/
@@ -6017,6 +6027,7 @@
PRIVATE_SETTINGS.add(ADVANCED_SETTINGS);
PRIVATE_SETTINGS.add(WEAR_ACCESSIBILITY_GESTURE_ENABLED);
PRIVATE_SETTINGS.add(WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE);
+ PRIVATE_SETTINGS.add(WEAR_TTS_PREWARM_ENABLED);
PRIVATE_SETTINGS.add(SCREEN_AUTO_BRIGHTNESS_ADJ);
PRIVATE_SETTINGS.add(VIBRATE_INPUT_DEVICES);
PRIVATE_SETTINGS.add(VOLUME_MASTER);
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 4e8d6e7..22b1f02 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -1,9 +1,9 @@
# Bug component: 36824
-cbrubaker@google.com
+brambonne@google.com
+brufino@google.com
+jeffv@google.com
-per-file NetworkSecurityPolicy.java = cbrubaker@google.com
-per-file NetworkSecurityPolicy.java = klyubin@google.com
-per-file FrameworkNetworkSecurityPolicy.java = cbrubaker@google.com
-per-file FrameworkNetworkSecurityPolicy.java = klyubin@google.com
+per-file *NetworkSecurityPolicy.java = file:net/OWNERS
per-file Confirmation*.java = file:/keystore/OWNERS
+per-file FileIntegrityManager.java = victorhsieh@google.com
diff --git a/core/java/android/security/net/OWNERS b/core/java/android/security/net/OWNERS
index d828164..1d52eed 100644
--- a/core/java/android/security/net/OWNERS
+++ b/core/java/android/security/net/OWNERS
@@ -1,4 +1,4 @@
# Bug component: 36824
-cbrubaker@google.com
brambonne@google.com
+jeffv@google.com
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 827600c..85f5395 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -247,6 +247,7 @@
DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true");
DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "true");
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS, "false");
+ DEFAULT_FLAGS.put("settings_press_hold_nav_handle_to_search", "false");
}
private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index 511cb2d..ac76fc2 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -212,6 +212,11 @@
return -1;
}
+ /** Returns {@code true} if this array contains the specified value. */
+ public boolean contains(int value) {
+ return indexOf(value) != -1;
+ }
+
/**
* Removes the value at the specified index from this array.
*/
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 6e73a3c..23afb03 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -346,13 +346,13 @@
}
private static boolean shouldTriggerStylusHandwritingForView(@NonNull View view) {
- if (!view.isAutoHandwritingEnabled()) {
+ if (!view.shouldInitiateHandwriting()) {
return false;
}
- // The view may be a handwriting initiation delegate, in which case it is not the editor
+ // The view may be a handwriting initiation delegator, in which case it is not the editor
// view for which handwriting would be started. However, in almost all cases, the return
- // values of View#isStylusHandwritingAvailable will be the same for the delegate view and
- // the delegator editor view. So the delegate view can be used to decide whether handwriting
+ // values of View#isStylusHandwritingAvailable will be the same for the delegator view and
+ // the delegate editor view. So the delegator view can be used to decide whether handwriting
// should be triggered.
return view.isStylusHandwritingAvailable();
}
@@ -677,7 +677,7 @@
/** The helper method to check if the given view is still active for handwriting. */
private static boolean isViewActive(@Nullable View view) {
return view != null && view.isAttachedToWindow() && view.isAggregatedVisible()
- && view.isAutoHandwritingEnabled();
+ && view.shouldInitiateHandwriting();
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5e19c67..8703609 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5488,7 +5488,6 @@
(TEXT_ALIGNMENT_DEFAULT << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT) |
(PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT) |
(IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT);
- mPrivateFlags4 = PFLAG4_AUTO_HANDWRITING_ENABLED;
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
@@ -6213,7 +6212,7 @@
setPreferKeepClear(a.getBoolean(attr, false));
break;
case R.styleable.View_autoHandwritingEnabled:
- setAutoHandwritingEnabled(a.getBoolean(attr, true));
+ setAutoHandwritingEnabled(a.getBoolean(attr, false));
break;
case R.styleable.View_handwritingBoundsOffsetLeft:
mHandwritingBoundsOffsetLeft = a.getDimension(attr, 0);
@@ -12150,7 +12149,7 @@
if (getSystemGestureExclusionRects().isEmpty()
&& collectPreferKeepClearRects().isEmpty()
&& collectUnrestrictedPreferKeepClearRects().isEmpty()
- && (info.mHandwritingArea == null || !isAutoHandwritingEnabled())) {
+ && (info.mHandwritingArea == null || !shouldInitiateHandwriting())) {
if (info.mPositionUpdateListener != null) {
mRenderNode.removePositionUpdateListener(info.mPositionUpdateListener);
info.mPositionUpdateListener = null;
@@ -12517,7 +12516,7 @@
void updateHandwritingArea() {
// If autoHandwritingArea is not enabled, do nothing.
- if (!isAutoHandwritingEnabled()) return;
+ if (!shouldInitiateHandwriting()) return;
final AttachInfo ai = mAttachInfo;
if (ai != null) {
ai.mViewRootImpl.getHandwritingInitiator().updateHandwritingAreasForView(this);
@@ -12525,6 +12524,16 @@
}
/**
+ * Returns true if a stylus {@link MotionEvent} within this view's bounds should initiate
+ * handwriting mode, either for this view ({@link #isAutoHandwritingEnabled()} is {@code true})
+ * or for a handwriting delegate view ({@link #getHandwritingDelegatorCallback()} is not {@code
+ * null}).
+ */
+ boolean shouldInitiateHandwriting() {
+ return isAutoHandwritingEnabled() || getHandwritingDelegatorCallback() != null;
+ }
+
+ /**
* Sets a callback which should be called when a stylus {@link MotionEvent} occurs within this
* view's bounds. The callback will be called from the UI thread.
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ddd3269..e0fda7e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4127,7 +4127,7 @@
}
private void fireAccessibilityFocusEventIfHasFocusedNode() {
- if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (!mAccessibilityManager.isEnabled()) {
return;
}
final View focusedView = mView.findFocus();
@@ -5179,8 +5179,12 @@
}
private boolean getAccessibilityFocusedRect(Rect bounds) {
- final AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext);
- if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) {
+ if (mView == null) {
+ Slog.w(TAG, "calling getAccessibilityFocusedRect() while the mView is null");
+ return false;
+ }
+ if (!mAccessibilityManager.isEnabled()
+ || !mAccessibilityManager.isTouchExplorationEnabled()) {
return false;
}
@@ -7248,8 +7252,8 @@
&& action != MotionEvent.ACTION_HOVER_EXIT) {
return;
}
- AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
- if (manager.isEnabled() && manager.isTouchExplorationEnabled()) {
+ if (mAccessibilityManager.isEnabled()
+ && mAccessibilityManager.isTouchExplorationEnabled()) {
return;
}
if (mView == null) {
@@ -11049,7 +11053,7 @@
return;
}
// The accessibility may be turned off while we were waiting so check again.
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (mAccessibilityManager.isEnabled()) {
mLastEventTimeMillis = SystemClock.uptimeMillis();
AccessibilityEvent event = AccessibilityEvent.obtain();
event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e1de05b..79acfbb 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -801,6 +801,11 @@
mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId,
itemsAction.mServiceIntent));
isActionReplaced = true;
+ } else if (action instanceof SetRemoteViewsAdapterIntent intentAction
+ && intentAction.viewId == viewId) {
+ mActions.set(i, new SetRemoteCollectionItemListAdapterAction(
+ intentAction.viewId, intentAction.intent));
+ isActionReplaced = true;
} else if (action instanceof ViewGroupActionAdd groupAction
&& groupAction.mNestedViews != null) {
isActionReplaced |= groupAction.mNestedViews.replaceRemoteCollections(viewId);
@@ -822,6 +827,42 @@
return isActionReplaced;
}
+ /**
+ * @return True if has set remote adapter using service intent
+ * @hide
+ */
+ public boolean hasLegacyLists() {
+ if (mActions != null) {
+ for (int i = 0; i < mActions.size(); i++) {
+ Action action = mActions.get(i);
+ if ((action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
+ && itemsAction.mServiceIntent != null)
+ || (action instanceof SetRemoteViewsAdapterIntent intentAction
+ && intentAction.intent != null)
+ || (action instanceof ViewGroupActionAdd groupAction
+ && groupAction.mNestedViews != null
+ && groupAction.mNestedViews.hasLegacyLists())) {
+ return true;
+ }
+ }
+ }
+ if (mSizedRemoteViews != null) {
+ for (int i = 0; i < mSizedRemoteViews.size(); i++) {
+ if (mSizedRemoteViews.get(i).hasLegacyLists()) {
+ return true;
+ }
+ }
+ }
+ if (mLandscape != null && mLandscape.hasLegacyLists()) {
+ return true;
+ }
+ if (mPortrait != null && mPortrait.hasLegacyLists()) {
+ return true;
+ }
+
+ return false;
+ }
+
private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
if (icon != null && (icon.getType() == Icon.TYPE_URI
|| icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8899f5c..0001fe6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1856,6 +1856,7 @@
boolean clickable = canInputOrMove || isClickable();
boolean longClickable = canInputOrMove || isLongClickable();
int focusable = getFocusable();
+ boolean isAutoHandwritingEnabled = true;
n = a.getIndexCount();
for (int i = 0; i < n; i++) {
@@ -1878,6 +1879,10 @@
case com.android.internal.R.styleable.View_longClickable:
longClickable = a.getBoolean(attr, longClickable);
break;
+
+ case com.android.internal.R.styleable.View_autoHandwritingEnabled:
+ isAutoHandwritingEnabled = a.getBoolean(attr, true);
+ break;
}
}
a.recycle();
@@ -1891,6 +1896,7 @@
}
setClickable(clickable);
setLongClickable(longClickable);
+ setAutoHandwritingEnabled(isAutoHandwritingEnabled);
if (mEditor != null) mEditor.prepareCursorControllers();
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
new file mode 100644
index 0000000..560e41b
--- /dev/null
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.window.flags"
+
+# Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
+
+flag {
+ namespace: "windowing_sdk"
+ name: "sync_window_config_update_flag"
+ description: "Whether the feature to sync different window-related config updates is enabled"
+ bug: "260873529"
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f55501a..ffd640f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2267,6 +2267,14 @@
<permission android:name="android.permission.MANAGE_ETHERNET_NETWORKS"
android:protectionLevel="signature" />
+ <!-- Allows system apps to call methods to register itself as a mDNS offload engine.
+ <p>Not for use by third-party or privileged applications.
+ @SystemApi
+ @hide This should only be used by system apps.
+ -->
+ <permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE"
+ android:protectionLevel="signature" />
+
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
<!-- ======================================= -->
diff --git a/core/res/res/anim-ldrtl/activity_close_enter.xml b/core/res/res/anim-ldrtl/activity_close_enter.xml
index 6a699e7..0b48646 100644
--- a/core/res/res/anim-ldrtl/activity_close_enter.xml
+++ b/core/res/res/anim-ldrtl/activity_close_enter.xml
@@ -31,7 +31,7 @@
android:duration="450" />
<translate
- android:fromXDelta="10%"
+ android:fromXDelta="96dp"
android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
@@ -41,11 +41,11 @@
android:duration="450" />
<extend
- android:fromExtendLeft="10%"
+ android:fromExtendLeft="96dp"
android:fromExtendTop="0"
android:fromExtendRight="0"
android:fromExtendBottom="0"
- android:toExtendLeft="10%"
+ android:toExtendLeft="96dp"
android:toExtendTop="0"
android:toExtendRight="0"
android:toExtendBottom="0"
diff --git a/core/res/res/anim-ldrtl/activity_close_exit.xml b/core/res/res/anim-ldrtl/activity_close_exit.xml
index 06a0d69..5277b9f 100644
--- a/core/res/res/anim-ldrtl/activity_close_exit.xml
+++ b/core/res/res/anim-ldrtl/activity_close_exit.xml
@@ -32,7 +32,7 @@
<translate
android:fromXDelta="0"
- android:toXDelta="-10%"
+ android:toXDelta="-96dp"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
@@ -43,11 +43,11 @@
<extend
android:fromExtendLeft="0"
android:fromExtendTop="0"
- android:fromExtendRight="10%"
+ android:fromExtendRight="96dp"
android:fromExtendBottom="0"
android:toExtendLeft="0"
android:toExtendTop="0"
- android:toExtendRight="10%"
+ android:toExtendRight="96dp"
android:toExtendBottom="0"
android:interpolator="@interpolator/fast_out_extra_slow_in"
android:startOffset="0"
diff --git a/core/res/res/anim-ldrtl/activity_open_enter.xml b/core/res/res/anim-ldrtl/activity_open_enter.xml
index 7b18294..97d2cf9 100644
--- a/core/res/res/anim-ldrtl/activity_open_enter.xml
+++ b/core/res/res/anim-ldrtl/activity_open_enter.xml
@@ -30,7 +30,7 @@
android:duration="83" />
<translate
- android:fromXDelta="-10%"
+ android:fromXDelta="-96dp"
android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
@@ -41,11 +41,11 @@
<extend
android:fromExtendLeft="0"
android:fromExtendTop="0"
- android:fromExtendRight="10%"
+ android:fromExtendRight="96dp"
android:fromExtendBottom="0"
android:toExtendLeft="0"
android:toExtendTop="0"
- android:toExtendRight="10%"
+ android:toExtendRight="96dp"
android:toExtendBottom="0"
android:interpolator="@interpolator/fast_out_extra_slow_in"
android:startOffset="0"
diff --git a/core/res/res/anim-ldrtl/activity_open_exit.xml b/core/res/res/anim-ldrtl/activity_open_exit.xml
index c29509e..2159029 100644
--- a/core/res/res/anim-ldrtl/activity_open_exit.xml
+++ b/core/res/res/anim-ldrtl/activity_open_exit.xml
@@ -31,7 +31,7 @@
<translate
android:fromXDelta="0"
- android:toXDelta="10%"
+ android:toXDelta="96dp"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
@@ -40,11 +40,11 @@
android:duration="450" />
<extend
- android:fromExtendLeft="10%"
+ android:fromExtendLeft="9dp"
android:fromExtendTop="0"
android:fromExtendRight="0"
android:fromExtendBottom="0"
- android:toExtendLeft="10%"
+ android:toExtendLeft="96dp"
android:toExtendTop="0"
android:toExtendRight="0"
android:toExtendBottom="0"
diff --git a/core/res/res/anim-ldrtl/task_fragment_close_exit.xml b/core/res/res/anim-ldrtl/task_fragment_close_exit.xml
index c5a3654..58fcb1f 100644
--- a/core/res/res/anim-ldrtl/task_fragment_close_exit.xml
+++ b/core/res/res/anim-ldrtl/task_fragment_close_exit.xml
@@ -30,7 +30,7 @@
<translate
android:fromXDelta="0"
- android:toXDelta="-10%"
+ android:toXDelta="-96dp"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
index 0fefb51..22a1dd6 100644
--- a/core/res/res/anim/activity_close_enter.xml
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -31,7 +31,7 @@
android:duration="450" />
<translate
- android:fromXDelta="-10%"
+ android:fromXDelta="-96dp"
android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
@@ -43,11 +43,11 @@
<extend
android:fromExtendLeft="0"
android:fromExtendTop="0"
- android:fromExtendRight="10%"
+ android:fromExtendRight="96dp"
android:fromExtendBottom="0"
android:toExtendLeft="0"
android:toExtendTop="0"
- android:toExtendRight="10%"
+ android:toExtendRight="96dp"
android:toExtendBottom="0"
android:interpolator="@interpolator/fast_out_extra_slow_in"
android:startOffset="0"
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
index f807c26..a671049 100644
--- a/core/res/res/anim/activity_close_exit.xml
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -32,7 +32,7 @@
<translate
android:fromXDelta="0"
- android:toXDelta="10%"
+ android:toXDelta="96dp"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
@@ -41,11 +41,11 @@
android:duration="450" />
<extend
- android:fromExtendLeft="10%"
+ android:fromExtendLeft="96dp"
android:fromExtendTop="0"
android:fromExtendRight="0"
android:fromExtendBottom="0"
- android:toExtendLeft="10%"
+ android:toExtendLeft="96dp"
android:toExtendTop="0"
android:toExtendRight="0"
android:toExtendBottom="0"
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
index 1674dab..f3172e4 100644
--- a/core/res/res/anim/activity_open_enter.xml
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -30,7 +30,7 @@
android:duration="83" />
<translate
- android:fromXDelta="10%"
+ android:fromXDelta="96dp"
android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
@@ -39,11 +39,11 @@
android:duration="450" />
<extend
- android:fromExtendLeft="10%"
+ android:fromExtendLeft="96dp"
android:fromExtendTop="0"
android:fromExtendRight="0"
android:fromExtendBottom="0"
- android:toExtendLeft="10%"
+ android:toExtendLeft="96dp"
android:toExtendTop="0"
android:toExtendRight="0"
android:toExtendBottom="0"
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
index 372f2c8..d84827b 100644
--- a/core/res/res/anim/activity_open_exit.xml
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -31,7 +31,7 @@
<translate
android:fromXDelta="0"
- android:toXDelta="-10%"
+ android:toXDelta="-96dp"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
@@ -42,11 +42,11 @@
<extend
android:fromExtendLeft="0"
android:fromExtendTop="0"
- android:fromExtendRight="10%"
+ android:fromExtendRight="96dp"
android:fromExtendBottom="0"
android:toExtendLeft="0"
android:toExtendTop="0"
- android:toExtendRight="10%"
+ android:toExtendRight="96dp"
android:toExtendBottom="0"
android:interpolator="@interpolator/fast_out_extra_slow_in"
android:startOffset="0"
diff --git a/core/res/res/anim/task_fragment_close_exit.xml b/core/res/res/anim/task_fragment_close_exit.xml
index 84d8b7e..6454085 100644
--- a/core/res/res/anim/task_fragment_close_exit.xml
+++ b/core/res/res/anim/task_fragment_close_exit.xml
@@ -30,7 +30,7 @@
<translate
android:fromXDelta="0"
- android:toXDelta="10%"
+ android:toXDelta="96dp"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
diff --git a/core/res/res/anim/task_fragment_open_enter.xml b/core/res/res/anim/task_fragment_open_enter.xml
index 87ee179..5f57ed5 100644
--- a/core/res/res/anim/task_fragment_open_enter.xml
+++ b/core/res/res/anim/task_fragment_open_enter.xml
@@ -27,7 +27,7 @@
android:startOffset="50"
android:duration="83" />
<translate
- android:fromXDelta="10%"
+ android:fromXDelta="96dp"
android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index aeeaaca..281053b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1210,13 +1210,16 @@
<integer name="config_triplePressOnStemPrimaryBehavior">0</integer>
<!-- Control the behavior when the user short presses the stem primary button.
- Stem primary button is only used on watch form factor. If a device is not
- a watch, setting this config is no-op.
- 0 - Nothing
- 1 - Go to launch all apps
+ Stem primary button is only used on watch form factor. If a device is not
+ a watch, setting this config is no-op.
+ 0 - Nothing
+ 1 - Go to launch all apps
+ 2 - Launch target activity defined by config_primaryShortPressTargetActivity if available
-->
<integer name="config_shortPressOnStemPrimaryBehavior">0</integer>
+ <!-- Activity name for the default target activity to be launched. [DO NOT TRANSLATE] -->
+ <string name="config_primaryShortPressTargetActivity" translatable="false"></string>
<!-- Control the behavior of the search key.
0 - Launch default search activity
@@ -5356,6 +5359,7 @@
<item>1,1,1.0,0,1</item>
<item>1,1,1.0,.4,1</item>
<item>1,1,1.0,.15,15</item>
+ <item>0,0,0.7,0,1</item>
</string-array>
<!-- The integer index of the selected option in config_udfps_touch_detection_options -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index bda194a..6bb87f3 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -73,7 +73,7 @@
CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
If 0, the device always switch to the higher score SIM.
If < 0, the network type and signal strength based auto switch is disabled. -->
- <integer name="auto_data_switch_score_tolerance">3000</integer>
+ <integer name="auto_data_switch_score_tolerance">-1</integer>
<java-symbol type="integer" name="auto_data_switch_score_tolerance" />
<!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8330f7b..aa0cbfa 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -466,6 +466,7 @@
<java-symbol type="integer" name="config_shortPressOnSleepBehavior" />
<java-symbol type="integer" name="config_longPressOnStemPrimaryBehavior" />
<java-symbol type="integer" name="config_shortPressOnStemPrimaryBehavior" />
+ <java-symbol type="string" name="config_primaryShortPressTargetActivity" />
<java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" />
<java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" />
<java-symbol type="string" name="config_doublePressOnPowerTargetActivity" />
diff --git a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
index 9acb99a..b794352 100644
--- a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
+++ b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
@@ -445,7 +445,9 @@
Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION)
}
handlerThread = HandlerThread("PackageSessionTests")
- handlerThread?.start()
- handler = Handler(handlerThread?.looper)
+ handlerThread?.let {
+ it.start()
+ handler = Handler(it.looper)
+ }
}
}
diff --git a/core/tests/coretests/res/values-id/strings.xml b/core/tests/coretests/res/values-id/strings.xml
new file mode 100644
index 0000000..6d71c90
--- /dev/null
+++ b/core/tests/coretests/res/values-id/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Used in ResourcesLocaleTest. -->
+ <string name="locale_test_res_1">Pengujian ID</string>
+</resources>
diff --git a/core/tests/coretests/res/values-in/strings.xml b/core/tests/coretests/res/values-in/strings.xml
new file mode 100644
index 0000000..6384660
--- /dev/null
+++ b/core/tests/coretests/res/values-in/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Used in ResourcesLocaleTest. -->
+ <string name="locale_test_res_2">Pengujian IN</string>
+</resources>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index e51eab6..09e1c69 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -131,6 +131,13 @@
<string name="textview_hebrew_text">םמab?!</string>
+ <!-- Used in ResourcesLocaleTest. Also defined in values-id. "id" is the new ISO code for Indonesian. -->
+ <string name="locale_test_res_1">Testing ID</string>
+ <!-- Used in ResourcesLocaleTest. Also defined in values-in. "in" is the deprecated ISO code for Indonesian. -->
+ <string name="locale_test_res_2">Testing IN</string>
+ <!-- Used in ResourcesLocaleTest. -->
+ <string name="locale_test_res_3">Testing EN</string>
+
<!-- SizeAdaptiveLayout -->
<string name="first">Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.</string>
<string name="actor">Abe Lincoln</string>
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
index 25c3db5..26e4349 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.content.Context;
import android.os.FileUtils;
import android.os.LocaleList;
import android.platform.test.annotations.Presubmit;
@@ -97,4 +98,24 @@
assertEquals(Locale.forLanguageTag("pl-PL"),
resources.getConfiguration().getLocales().get(0));
}
+
+ @SmallTest
+ public void testDeprecatedISOLanguageCode() {
+ assertResGetString(Locale.US, R.string.locale_test_res_1, "Testing ID");
+ assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_2, "Pengujian IN");
+ assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_3, "Testing EN");
+ assertResGetString(new Locale("id"), R.string.locale_test_res_2, "Pengujian IN");
+ assertResGetString(new Locale("id"), R.string.locale_test_res_3, "Testing EN");
+ // The new ISO code "id" isn't supported yet, and thus the values-id are ignored.
+ assertResGetString(new Locale("id"), R.string.locale_test_res_1, "Testing ID");
+ assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_1, "Testing ID");
+ }
+
+ private void assertResGetString(Locale locale, int resId, String expectedString) {
+ LocaleList locales = new LocaleList(locale);
+ final Configuration config = new Configuration();
+ config.setLocales(locales);
+ Context newContext = getContext().createConfigurationContext(config);
+ assertEquals(expectedString, newContext.getResources().getString(resId));
+ }
}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 394ff0a..a0d8183 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -505,45 +505,32 @@
@Test
public void testRoundStorageSize() throws Exception {
- final long GB1 = DataUnit.GIGABYTES.toBytes(1);
- final long GiB1 = DataUnit.GIBIBYTES.toBytes(1);
- final long GB2 = DataUnit.GIGABYTES.toBytes(2);
- final long GiB2 = DataUnit.GIBIBYTES.toBytes(2);
- final long GiB128 = DataUnit.GIBIBYTES.toBytes(128);
- final long GB256 = DataUnit.GIGABYTES.toBytes(256);
- final long GiB256 = DataUnit.GIBIBYTES.toBytes(256);
- final long GB512 = DataUnit.GIGABYTES.toBytes(512);
- final long GiB512 = DataUnit.GIBIBYTES.toBytes(512);
- final long TB1 = DataUnit.TERABYTES.toBytes(1);
- final long TiB1 = DataUnit.TEBIBYTES.toBytes(1);
- final long TB2 = DataUnit.TERABYTES.toBytes(2);
- final long TiB2 = DataUnit.TEBIBYTES.toBytes(2);
- final long TB4 = DataUnit.TERABYTES.toBytes(4);
- final long TiB4 = DataUnit.TEBIBYTES.toBytes(4);
- final long TB8 = DataUnit.TERABYTES.toBytes(8);
- final long TiB8 = DataUnit.TEBIBYTES.toBytes(8);
+ final long M256 = DataUnit.MEGABYTES.toBytes(256);
+ final long M512 = DataUnit.MEGABYTES.toBytes(512);
+ final long G1 = DataUnit.GIGABYTES.toBytes(1);
+ final long G2 = DataUnit.GIGABYTES.toBytes(2);
+ final long G32 = DataUnit.GIGABYTES.toBytes(32);
+ final long G64 = DataUnit.GIGABYTES.toBytes(64);
+ final long G512 = DataUnit.GIGABYTES.toBytes(512);
+ final long G1000 = DataUnit.TERABYTES.toBytes(1);
+ final long G2000 = DataUnit.TERABYTES.toBytes(2);
- assertEquals(GB1, roundStorageSize(GB1 - 1));
- assertEquals(GB1, roundStorageSize(GB1));
- assertEquals(GB1, roundStorageSize(GB1 + 1));
- assertEquals(GB1, roundStorageSize(GiB1 - 1));
- assertEquals(GB1, roundStorageSize(GiB1));
- assertEquals(GB2, roundStorageSize(GiB1 + 1));
- assertEquals(GB2, roundStorageSize(GiB2));
+ assertEquals(M256, roundStorageSize(M256 - 1));
+ assertEquals(M256, roundStorageSize(M256));
+ assertEquals(M512, roundStorageSize(M256 + 1));
+ assertEquals(M512, roundStorageSize(M512 - 1));
+ assertEquals(M512, roundStorageSize(M512));
+ assertEquals(G1, roundStorageSize(M512 + 1));
+ assertEquals(G1, roundStorageSize(G1));
+ assertEquals(G2, roundStorageSize(G1 + 1));
- assertEquals(GB256, roundStorageSize(GiB128 + 1));
- assertEquals(GB256, roundStorageSize(GiB256));
- assertEquals(GB512, roundStorageSize(GiB256 + 1));
- assertEquals(GB512, roundStorageSize(GiB512));
- assertEquals(TB1, roundStorageSize(GiB512 + 1));
- assertEquals(TB1, roundStorageSize(TiB1));
- assertEquals(TB2, roundStorageSize(TiB1 + 1));
- assertEquals(TB2, roundStorageSize(TiB2));
- assertEquals(TB4, roundStorageSize(TiB2 + 1));
- assertEquals(TB4, roundStorageSize(TiB4));
- assertEquals(TB8, roundStorageSize(TiB4 + 1));
- assertEquals(TB8, roundStorageSize(TiB8));
- assertEquals(TB1, roundStorageSize(1013077688320L)); // b/268571529
+ assertEquals(G32, roundStorageSize(G32 - 1));
+ assertEquals(G32, roundStorageSize(G32));
+ assertEquals(G64, roundStorageSize(G32 + 1));
+
+ assertEquals(G512, roundStorageSize(G512 - 1));
+ assertEquals(G1000, roundStorageSize(G512 + 1));
+ assertEquals(G2000, roundStorageSize(G1000 + 1));
}
@Test
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index f1eef75..c46118d 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -245,7 +245,7 @@
@Test
public void onTouchEvent_tryAcceptDelegation_delegatorCallbackCreatesInputConnection() {
- View delegateView = new View(mContext);
+ View delegateView = new EditText(mContext);
delegateView.setIsHandwritingDelegate(true);
mTestView1.setHandwritingDelegatorCallback(
@@ -266,7 +266,7 @@
@Test
public void onTouchEvent_tryAcceptDelegation_delegatorCallbackFocusesDelegate() {
- View delegateView = new View(mContext);
+ View delegateView = new EditText(mContext);
delegateView.setIsHandwritingDelegate(true);
mHandwritingInitiator.onInputConnectionCreated(delegateView);
reset(mHandwritingInitiator);
diff --git a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
new file mode 100644
index 0000000..a8b4032
--- /dev/null
+++ b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window.flags;
+
+import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
+
+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;
+
+/**
+ * Tests for {@link com.android.window.flags.Flags}
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowFlagsTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowFlagsTest {
+
+ @Test
+ public void testSyncWindowConfigUpdateFlag() {
+ // No crash when accessing the flag.
+ syncWindowConfigUpdateFlag();
+ }
+}
diff --git a/core/tests/utiltests/src/android/util/IntArrayTest.java b/core/tests/utiltests/src/android/util/IntArrayTest.java
index caa7312..ad5c4ee 100644
--- a/core/tests/utiltests/src/android/util/IntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/IntArrayTest.java
@@ -55,9 +55,11 @@
a.add(5, 20);
assertThat(a.get(5)).isEqualTo(20);
assertThat(a.indexOf(20)).isEqualTo(5);
+ assertThat(a.contains(20)).isTrue();
verify(a, 1, 2, 0, 0, 0, 20, 10, 0, 0);
assertThat(a.indexOf(99)).isEqualTo(-1);
+ assertThat(a.contains(99)).isFalse();
a.resize(15);
a.set(14, 30);
@@ -71,6 +73,7 @@
backingArray[2] = 30;
verify(a, backingArray);
assertThat(a.indexOf(30)).isEqualTo(2);
+ assertThat(a.contains(30)).isTrue();
a.resize(2);
assertThat(backingArray[2]).isEqualTo(0);
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index e27cd97..31092536 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -439,8 +439,10 @@
key usage 0x0c0173 MEDIA_AUDIO_TRACK
key usage 0x0c019C PROFILE_SWITCH
key usage 0x0c01A2 ALL_APPS
-key usage 0x0d0044 STYLUS_BUTTON_PRIMARY
-key usage 0x0d005a STYLUS_BUTTON_SECONDARY
+# TODO(b/297094448): Add stylus button mappings as a fallback when we have a way to determine
+# if a device can actually report it.
+# key usage 0x0d0044 STYLUS_BUTTON_PRIMARY
+# key usage 0x0d005a STYLUS_BUTTON_SECONDARY
# Joystick and game controller axes.
# Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 81384ca..a5ee19e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -261,10 +261,6 @@
// Updates the Split
final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
final WindowContainerTransaction wct = transactionRecord.getTransaction();
-
- mPresenter.setTaskFragmentIsolatedNavigation(wct,
- splitPinContainer.getSecondaryContainer().getTaskFragmentToken(),
- true /* isolatedNav */);
mPresenter.updateSplitContainer(splitPinContainer, wct);
transactionRecord.apply(false /* shouldApplyIndependently */);
updateCallbackIfNecessary();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 5de6acf..896fe61 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -382,6 +382,19 @@
}
setCompanionTaskFragment(wct, primaryContainer.getTaskFragmentToken(),
secondaryContainer.getTaskFragmentToken(), splitRule, isStacked);
+
+ // Setting isolated navigation and clear non-sticky pinned container if needed.
+ final SplitPinRule splitPinRule =
+ splitRule instanceof SplitPinRule ? (SplitPinRule) splitRule : null;
+ if (splitPinRule == null) {
+ return;
+ }
+
+ setTaskFragmentIsolatedNavigation(wct, secondaryContainer.getTaskFragmentToken(),
+ !isStacked /* isolatedNav */);
+ if (isStacked && !splitPinRule.isSticky()) {
+ secondaryContainer.getTaskContainer().removeSplitPinContainer();
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 4640106..9b80063 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -245,8 +245,8 @@
private boolean shouldShowBackdrop(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change) {
- final Animation a = loadAttributeAnimation(info, change, WALLPAPER_TRANSITION_NONE,
- mTransitionAnimation, false);
+ final Animation a = loadAttributeAnimation(info.getType(), info, change,
+ WALLPAPER_TRANSITION_NONE, mTransitionAnimation, false);
return a != null && a.getShowBackdrop();
}
}
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 ff67110..9a2b812 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
@@ -851,7 +851,10 @@
return mAppIntent;
}
- boolean isAppBubble() {
+ /**
+ * Returns whether this bubble is from an app versus a notification.
+ */
+ public boolean isAppBubble() {
return mIsAppBubble;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
index 250e010..76662c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
@@ -52,6 +52,11 @@
private static final boolean FORCE_SHOW_USER_EDUCATION = false;
private static final String FORCE_SHOW_USER_EDUCATION_SETTING =
"force_show_bubbles_user_education";
+ /**
+ * When set to true, bubbles user education flow never shows up.
+ */
+ private static final String FORCE_HIDE_USER_EDUCATION_SETTING =
+ "force_hide_bubbles_user_education";
/**
* @return whether we should force show user education for bubbles. Used for debugging & demos.
@@ -62,6 +67,14 @@
return FORCE_SHOW_USER_EDUCATION || forceShow;
}
+ /**
+ * @return whether we should never show user education for bubbles. Used in tests.
+ */
+ static boolean neverShowUserEducation(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ FORCE_HIDE_USER_EDUCATION_SETTING, 0) != 0;
+ }
+
static String formatBubblesString(List<Bubble> bubbles, BubbleViewProvider selected) {
StringBuilder sb = new StringBuilder();
for (Bubble bubble : bubbles) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 2c10065..ea7053d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -653,14 +653,38 @@
}
/**
- * @return the stack position to use if we don't have a saved location or if user education
- * is being shown.
+ * Returns whether the {@link #getRestingPosition()} is equal to the default start position
+ * initialized for bubbles, if {@code true} this means the user hasn't moved the bubble
+ * from the initial start position (or they haven't received a bubble yet).
+ */
+ public boolean hasUserModifiedDefaultPosition() {
+ PointF defaultStart = getDefaultStartPosition();
+ return mRestingStackPosition != null
+ && !mRestingStackPosition.equals(defaultStart);
+ }
+
+ /**
+ * Returns the stack position to use if we don't have a saved location or if user education
+ * is being shown, for a normal bubble.
*/
public PointF getDefaultStartPosition() {
- // Start on the left if we're in LTR, right otherwise.
- final boolean startOnLeft =
- mContext.getResources().getConfiguration().getLayoutDirection()
- != LAYOUT_DIRECTION_RTL;
+ return getDefaultStartPosition(false /* isAppBubble */);
+ }
+
+ /**
+ * The stack position to use if we don't have a saved location or if user education
+ * is being shown.
+ *
+ * @param isAppBubble whether this start position is for an app bubble or not.
+ */
+ public PointF getDefaultStartPosition(boolean isAppBubble) {
+ final int layoutDirection = mContext.getResources().getConfiguration().getLayoutDirection();
+ // Normal bubbles start on the left if we're in LTR, right otherwise.
+ // TODO (b/294284894): update language around "app bubble" here
+ // App bubbles start on the right in RTL, left otherwise.
+ final boolean startOnLeft = isAppBubble
+ ? layoutDirection == LAYOUT_DIRECTION_RTL
+ : layoutDirection != LAYOUT_DIRECTION_RTL;
final RectF allowableStackPositionRegion = getAllowableStackPositionRegion(
1 /* default starts with 1 bubble */);
if (isLargeScreen()) {
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 8ae12c7..52c9bf8 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
@@ -1284,6 +1284,12 @@
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
Log.d(TAG, "Show manage edu: " + shouldShow);
}
+ if (shouldShow && BubbleDebugConfig.neverShowUserEducation(mContext)) {
+ if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "Want to show manage edu, but it is forced hidden");
+ }
+ return false;
+ }
return shouldShow;
}
@@ -1316,6 +1322,12 @@
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
Log.d(TAG, "Show stack edu: " + shouldShow);
}
+ if (shouldShow && BubbleDebugConfig.neverShowUserEducation(mContext)) {
+ if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "Want to show stack edu, but it is forced hidden");
+ }
+ return false;
+ }
return shouldShow;
}
@@ -1763,13 +1775,26 @@
return;
}
+ if (firstBubble && bubble.isAppBubble() && !mPositioner.hasUserModifiedDefaultPosition()) {
+ // TODO (b/294284894): update language around "app bubble" here
+ // If it's an app bubble and we don't have a previous resting position, update the
+ // controllers to use the default position for the app bubble (it'd be different from
+ // the position initialized with the controllers originally).
+ PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
+ mStackOnLeftOrWillBe = mPositioner.isStackOnLeft(startPosition);
+ mStackAnimationController.setStackPosition(startPosition);
+ mExpandedAnimationController.setCollapsePoint(startPosition);
+ // Set the translation x so that this bubble will animate in from the same side they
+ // expand / collapse on.
+ bubble.getIconView().setTranslationX(startPosition.x);
+ } else if (firstBubble) {
+ mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
+ }
+
mBubbleContainer.addView(bubble.getIconView(), 0,
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
mPositioner.getBubbleSize()));
- if (firstBubble) {
- mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
- }
// Set the dot position to the opposite of the side the stack is resting on, since the stack
// resting slightly off-screen would result in the dot also being off-screen.
bubble.getIconView().setDotBadgeOnLeft(!mStackOnLeftOrWillBe /* onLeft */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index c20733a..4d7042b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -131,6 +131,16 @@
private BubbleStackView mBubbleStackView;
+ /**
+ * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
+ * the rest of the bubbles to animate to fill the gap.
+ */
+ private boolean mBubbleDraggedOutEnough = false;
+
+ /** End action to run when the lead bubble's expansion animation completes. */
+ @Nullable
+ private Runnable mLeadBubbleEndAction;
+
public ExpandedAnimationController(BubblePositioner positioner,
Runnable onBubbleAnimatedOutAction, BubbleStackView stackView) {
mPositioner = positioner;
@@ -141,14 +151,12 @@
}
/**
- * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
- * the rest of the bubbles to animate to fill the gap.
+ * Overrides the collapse location without actually collapsing the stack.
+ * @param point the new collapse location.
*/
- private boolean mBubbleDraggedOutEnough = false;
-
- /** End action to run when the lead bubble's expansion animation completes. */
- @Nullable
- private Runnable mLeadBubbleEndAction;
+ public void setCollapsePoint(PointF point) {
+ mCollapsePoint = point;
+ }
/**
* Animates expanding the bubbles into a row along the top of the screen, optionally running an
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 4bb1ab4..aad2683 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
@@ -297,9 +297,6 @@
/** Whether the stack is on the left side of the screen. */
public boolean isStackOnLeftSide() {
- if (mLayout == null || !isStackPositionSet()) {
- return true; // Default to left, which is where it starts by default.
- }
return mPositioner.isStackOnLeft(mStackPosition);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
index 8b5283d..1fd22d0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
@@ -85,7 +85,7 @@
canvas.drawPath(path, paint)
}
- override fun onBoundsChange(bounds: Rect?) {
+ override fun onBoundsChange(bounds: Rect) {
requestPathUpdate()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
index a141ff9..4abb35c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.content.pm.PackageManager
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.pip.PipUtils
class PipAppOpsListener(
private val mContext: Context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
index 2719cd2..427a555 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
@@ -33,7 +33,6 @@
import android.os.HandlerExecutor
import android.os.UserHandle
import com.android.wm.shell.R
-import com.android.wm.shell.pip.PipUtils
import java.util.function.Consumer
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
new file mode 100644
index 0000000..84feb03
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common.pip
+
+import android.app.ActivityTaskManager
+import android.app.RemoteAction
+import android.app.WindowConfiguration
+import android.content.ComponentName
+import android.content.Context
+import android.os.RemoteException
+import android.os.SystemProperties
+import android.util.DisplayMetrics
+import android.util.Log
+import android.util.Pair
+import android.util.TypedValue
+import android.window.TaskSnapshot
+import com.android.internal.protolog.common.ProtoLog
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import kotlin.math.abs
+
+/** A class that includes convenience methods. */
+object PipUtils {
+ private const val TAG = "PipUtils"
+
+ // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
+ private const val EPSILON = 1e-7
+ private const val ENABLE_PIP2_IMPLEMENTATION = "persist.wm.debug.enable_pip2_implementation"
+
+ /**
+ * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
+ * The component name may be null if no such activity exists.
+ */
+ @JvmStatic
+ fun getTopPipActivity(context: Context): Pair<ComponentName?, Int> {
+ try {
+ val sysUiPackageName = context.packageName
+ val pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
+ WindowConfiguration.WINDOWING_MODE_PINNED,
+ WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+ )
+ if (pinnedTaskInfo?.childTaskIds != null && pinnedTaskInfo.childTaskIds.isNotEmpty()) {
+ for (i in pinnedTaskInfo.childTaskNames.indices.reversed()) {
+ val cn = ComponentName.unflattenFromString(
+ pinnedTaskInfo.childTaskNames[i]
+ )
+ if (cn != null && cn.packageName != sysUiPackageName) {
+ return Pair(cn, pinnedTaskInfo.childTaskUserIds[i])
+ }
+ }
+ }
+ } catch (e: RemoteException) {
+ ProtoLog.w(
+ ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Unable to get pinned stack.", TAG
+ )
+ }
+ return Pair(null, 0)
+ }
+
+ /**
+ * @return the pixels for a given dp value.
+ */
+ @JvmStatic
+ fun dpToPx(dpValue: Float, dm: DisplayMetrics?): Int {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, dm).toInt()
+ }
+
+ /**
+ * @return true if the aspect ratios differ
+ */
+ @JvmStatic
+ fun aspectRatioChanged(aspectRatio1: Float, aspectRatio2: Float): Boolean {
+ return abs(aspectRatio1 - aspectRatio2) > EPSILON
+ }
+
+ /**
+ * Checks whether title, description and intent match.
+ * Comparing icons would be good, but using equals causes false negatives
+ */
+ @JvmStatic
+ fun remoteActionsMatch(action1: RemoteAction?, action2: RemoteAction?): Boolean {
+ if (action1 === action2) return true
+ if (action1 == null || action2 == null) return false
+ return action1.isEnabled == action2.isEnabled &&
+ action1.shouldShowIcon() == action2.shouldShowIcon() &&
+ action1.title == action2.title &&
+ action1.contentDescription == action2.contentDescription &&
+ action1.actionIntent == action2.actionIntent
+ }
+
+ /**
+ * Returns true if the actions in the lists match each other according to
+ * [ ][PipUtils.remoteActionsMatch], including their position.
+ */
+ @JvmStatic
+ fun remoteActionsChanged(list1: List<RemoteAction?>?, list2: List<RemoteAction?>?): Boolean {
+ if (list1 == null && list2 == null) {
+ return false
+ }
+ if (list1 == null || list2 == null) {
+ return true
+ }
+ if (list1.size != list2.size) {
+ return true
+ }
+ for (i in list1.indices) {
+ if (!remoteActionsMatch(list1[i], list2[i])) {
+ return true
+ }
+ }
+ return false
+ }
+
+ /** @return [TaskSnapshot] for a given task id.
+ */
+ @JvmStatic
+ fun getTaskSnapshot(taskId: Int, isLowResolution: Boolean): TaskSnapshot? {
+ return if (taskId <= 0) null else try {
+ ActivityTaskManager.getService().getTaskSnapshot(
+ taskId, isLowResolution, false /* takeSnapshotIfNeeded */
+ )
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Failed to get task snapshot, taskId=$taskId", e)
+ null
+ }
+ }
+
+ @JvmStatic
+ val isPip2ExperimentEnabled: Boolean
+ get() = SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false)
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 7bf0893..c111ce6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -34,6 +34,7 @@
import android.view.Display;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -59,6 +60,7 @@
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -80,6 +82,9 @@
private static final String TAG = "CompatUIController";
+ // The time to wait before education and button hiding
+ private static final int DISAPPEAR_DELAY_MS = 5000;
+
/** Whether the IME is shown on display id. */
private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
@@ -158,6 +163,9 @@
@NonNull
private final CompatUIShellCommandHandler mCompatUIShellCommandHandler;
+ @NonNull
+ private final Function<Integer, Integer> mDisappearTimeSupplier;
+
@Nullable
private CompatUICallback mCompatUICallback;
@@ -176,7 +184,8 @@
@NonNull Lazy<Transitions> transitionsLazy,
@NonNull DockStateReader dockStateReader,
@NonNull CompatUIConfiguration compatUIConfiguration,
- @NonNull CompatUIShellCommandHandler compatUIShellCommandHandler) {
+ @NonNull CompatUIShellCommandHandler compatUIShellCommandHandler,
+ @NonNull AccessibilityManager accessibilityManager) {
mContext = context;
mShellController = shellController;
mDisplayController = displayController;
@@ -189,6 +198,8 @@
mDockStateReader = dockStateReader;
mCompatUIConfiguration = compatUIConfiguration;
mCompatUIShellCommandHandler = compatUIShellCommandHandler;
+ mDisappearTimeSupplier = flags -> accessibilityManager.getRecommendedTimeoutMillis(
+ DISAPPEAR_DELAY_MS, flags);
shellInit.addInitCallback(this::onInit, this);
}
@@ -510,7 +521,8 @@
ShellTaskOrganizer.TaskListener taskListener) {
return new ReachabilityEduWindowManager(context, taskInfo, mSyncQueue,
taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
- mCompatUIConfiguration, mMainExecutor, this::onInitialReachabilityEduDismissed);
+ mCompatUIConfiguration, mMainExecutor, this::onInitialReachabilityEduDismissed,
+ mDisappearTimeSupplier);
}
private void onInitialReachabilityEduDismissed(@NonNull TaskInfo taskInfo,
@@ -556,7 +568,8 @@
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
return new UserAspectRatioSettingsWindowManager(context, taskInfo, mSyncQueue,
taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
- mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor);
+ mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor,
+ mDisappearTimeSupplier);
}
private void launchUserAspectRatioSettings(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
index 9de3f9d..5612bc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
@@ -28,6 +28,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
@@ -37,15 +38,13 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import java.util.function.BiConsumer;
+import java.util.function.Function;
/**
* Window manager for the reachability education
*/
class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
- // The time to wait before hiding the education
- private static final long DISAPPEAR_DELAY_MS = 4000L;
-
private static final int REACHABILITY_LEFT_OR_UP_POSITION = 0;
private static final int REACHABILITY_RIGHT_OR_BOTTOM_POSITION = 2;
@@ -77,6 +76,8 @@
private final BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnDismissCallback;
+ private final Function<Integer, Integer> mDisappearTimeSupplier;
+
@Nullable
@VisibleForTesting
ReachabilityEduLayout mLayout;
@@ -85,7 +86,8 @@
SyncTransactionQueue syncQueue,
ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
CompatUIConfiguration compatUIConfiguration, ShellExecutor mainExecutor,
- BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onDismissCallback) {
+ BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onDismissCallback,
+ Function<Integer, Integer> disappearTimeSupplier) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mIsActivityLetterboxed = taskInfo.isLetterboxDoubleTapEnabled;
mLetterboxVerticalPosition = taskInfo.topActivityLetterboxVerticalPosition;
@@ -95,6 +97,7 @@
mCompatUIConfiguration = compatUIConfiguration;
mMainExecutor = mainExecutor;
mOnDismissCallback = onDismissCallback;
+ mDisappearTimeSupplier = disappearTimeSupplier;
}
@Override
@@ -215,7 +218,12 @@
}
void updateHideTime() {
- mNextHideTime = SystemClock.uptimeMillis() + DISAPPEAR_DELAY_MS;
+ mNextHideTime = SystemClock.uptimeMillis() + getDisappearTimeMs();
+ }
+
+ private long getDisappearTimeMs() {
+ return mDisappearTimeSupplier.apply(
+ AccessibilityManager.FLAG_CONTENT_ICONS | AccessibilityManager.FLAG_CONTENT_TEXT);
}
private void updateVisibilityOfViews() {
@@ -248,14 +256,15 @@
availableHeight, mCompatUIConfiguration, lastTaskInfo);
if (!mHasLetterboxSizeChanged) {
updateHideTime();
- mMainExecutor.executeDelayed(this::hideReachability, DISAPPEAR_DELAY_MS);
+ final long disappearTimeMs = getDisappearTimeMs();
+ mMainExecutor.executeDelayed(this::hideReachability, disappearTimeMs);
// If reachability education has been seen for the first time, trigger callback to
// display aspect ratio settings button once reachability education disappears
if (hasShownHorizontalReachabilityEduFirstTime(hasSeenHorizontalReachabilityEdu)
|| hasShownVerticalReachabilityEduFirstTime(
hasSeenVerticalReachabilityEdu)) {
mMainExecutor.executeDelayed(this::triggerOnDismissCallback,
- DISAPPEAR_DELAY_MS);
+ disappearTimeMs);
}
}
mHasUserDoubleTapped = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index bd53dc7..cbff464 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -26,6 +26,7 @@
import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
@@ -36,6 +37,7 @@
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
import java.util.function.BiConsumer;
+import java.util.function.Function;
/**
* Window manager for the user aspect ratio settings button which allows users to go to
@@ -45,12 +47,12 @@
private static final long SHOW_USER_ASPECT_RATIO_BUTTON_DELAY_MS = 500L;
- private static final long HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS = 4000L;
-
private long mNextButtonHideTimeMs = -1L;
private final BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnButtonClicked;
+ private final Function<Integer, Integer> mDisappearTimeSupplier;
+
private final ShellExecutor mShellExecutor;
@VisibleForTesting
@@ -69,12 +71,14 @@
@Nullable ShellTaskOrganizer.TaskListener taskListener,
@NonNull DisplayLayout displayLayout, @NonNull CompatUIHintsState compatUIHintsState,
@NonNull BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onButtonClicked,
- @NonNull ShellExecutor shellExecutor) {
+ @NonNull ShellExecutor shellExecutor,
+ @NonNull Function<Integer, Integer> disappearTimeSupplier) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mShellExecutor = shellExecutor;
mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
mCompatUIHintsState = compatUIHintsState;
mOnButtonClicked = onButtonClicked;
+ mDisappearTimeSupplier = disappearTimeSupplier;
}
@Override
@@ -140,9 +144,9 @@
return;
}
mLayout.setUserAspectRatioSettingsHintVisibility(/* show= */ true);
- mNextButtonHideTimeMs = updateHideTime(HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
- mShellExecutor.executeDelayed(this::hideUserAspectRatioButton,
- HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
+ final long disappearTimeMs = getDisappearTimeMs();
+ mNextButtonHideTimeMs = updateHideTime(disappearTimeMs);
+ mShellExecutor.executeDelayed(this::hideUserAspectRatioButton, disappearTimeMs);
}
@Override
@@ -167,9 +171,9 @@
if (mHasUserAspectRatioSettingsButton) {
mShellExecutor.executeDelayed(this::showUserAspectRatioButton,
SHOW_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
- mNextButtonHideTimeMs = updateHideTime(HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
- mShellExecutor.executeDelayed(this::hideUserAspectRatioButton,
- HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
+ final long disappearTimeMs = getDisappearTimeMs();
+ mNextButtonHideTimeMs = updateHideTime(disappearTimeMs);
+ mShellExecutor.executeDelayed(this::hideUserAspectRatioButton, disappearTimeMs);
} else {
mShellExecutor.removeCallbacks(this::showUserAspectRatioButton);
mShellExecutor.execute(this::hideUserAspectRatioButton);
@@ -208,4 +212,8 @@
&& (taskInfo.topActivityBoundsLetterboxed
|| taskInfo.isUserFullscreenOverrideEnabled);
}
+
+ private long getDisappearTimeMs() {
+ return mDisappearTimeSupplier.apply(AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 9472341..e28b8d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -24,6 +24,7 @@
import android.os.Handler;
import android.os.SystemProperties;
import android.view.IWindowManager;
+import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
@@ -233,10 +234,12 @@
DisplayImeController imeController, SyncTransactionQueue syncQueue,
@ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy,
DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration,
- CompatUIShellCommandHandler compatUIShellCommandHandler) {
+ CompatUIShellCommandHandler compatUIShellCommandHandler,
+ AccessibilityManager accessibilityManager) {
return new CompatUIController(context, shellInit, shellController, displayController,
displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy,
- dockStateReader, compatUIConfiguration, compatUIShellCommandHandler);
+ dockStateReader, compatUIConfiguration, compatUIShellCommandHandler,
+ accessibilityManager);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 24ef44a..4e92ca1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -34,6 +34,7 @@
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
@@ -50,7 +51,6 @@
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
index 04032bb1..9c9364e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
@@ -18,9 +18,9 @@
import android.annotation.Nullable;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipUtils;
import dagger.Module;
import dagger.Provides;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 633f627..b0f75c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -311,7 +311,7 @@
)
val wct = WindowContainerTransaction()
wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
- wct.setBounds(task.token, null)
+ wct.setBounds(task.token, Rect())
wct.setDensityDpi(task.token, getDefaultDensityDpi())
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index ac711ea..4fef672 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -28,6 +28,7 @@
import android.view.Gravity;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import java.io.PrintWriter;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
index 456f85b..4aa260b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.pip;
-import static com.android.wm.shell.pip.PipUtils.dpToPx;
+import static com.android.wm.shell.common.pip.PipUtils.dpToPx;
import android.content.Context;
import android.content.res.Resources;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 296857b..ed9ff1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -83,6 +83,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index db7e2c0..83e03dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -64,6 +64,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 0f74f9e..64bba67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -38,6 +38,7 @@
import androidx.annotation.NonNull;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
deleted file mode 100644
index 3cd9848..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ /dev/null
@@ -1,145 +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.pip;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-
-import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManager.RootTaskInfo;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.Pair;
-import android.util.TypedValue;
-import android.window.TaskSnapshot;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
-
-import java.util.List;
-import java.util.Objects;
-
-/** A class that includes convenience methods. */
-public class PipUtils {
- private static final String TAG = "PipUtils";
-
- // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
- private static final double EPSILON = 1e-7;
-
- private static final String ENABLE_PIP2_IMPLEMENTATION =
- "persist.wm.debug.enable_pip2_implementation";
-
- /**
- * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
- * The component name may be null if no such activity exists.
- */
- public static Pair<ComponentName, Integer> getTopPipActivity(Context context) {
- try {
- final String sysUiPackageName = context.getPackageName();
- final RootTaskInfo pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
- if (pinnedTaskInfo != null && pinnedTaskInfo.childTaskIds != null
- && pinnedTaskInfo.childTaskIds.length > 0) {
- for (int i = pinnedTaskInfo.childTaskNames.length - 1; i >= 0; i--) {
- ComponentName cn = ComponentName.unflattenFromString(
- pinnedTaskInfo.childTaskNames[i]);
- if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) {
- return new Pair<>(cn, pinnedTaskInfo.childTaskUserIds[i]);
- }
- }
- }
- } catch (RemoteException e) {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Unable to get pinned stack.", TAG);
- }
- return new Pair<>(null, 0);
- }
-
- /**
- * @return the pixels for a given dp value.
- */
- public static int dpToPx(float dpValue, DisplayMetrics dm) {
- return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
- }
-
- /**
- * @return true if the aspect ratios differ
- */
- public static boolean aspectRatioChanged(float aspectRatio1, float aspectRatio2) {
- return Math.abs(aspectRatio1 - aspectRatio2) > EPSILON;
- }
-
- /**
- * Checks whether title, description and intent match.
- * Comparing icons would be good, but using equals causes false negatives
- */
- public static boolean remoteActionsMatch(RemoteAction action1, RemoteAction action2) {
- if (action1 == action2) return true;
- if (action1 == null || action2 == null) return false;
- return action1.isEnabled() == action2.isEnabled()
- && action1.shouldShowIcon() == action2.shouldShowIcon()
- && Objects.equals(action1.getTitle(), action2.getTitle())
- && Objects.equals(action1.getContentDescription(), action2.getContentDescription())
- && Objects.equals(action1.getActionIntent(), action2.getActionIntent());
- }
-
- /**
- * Returns true if the actions in the lists match each other according to {@link
- * PipUtils#remoteActionsMatch(RemoteAction, RemoteAction)}, including their position.
- */
- public static boolean remoteActionsChanged(List<RemoteAction> list1, List<RemoteAction> list2) {
- if (list1 == null && list2 == null) {
- return false;
- }
- if (list1 == null || list2 == null) {
- return true;
- }
- if (list1.size() != list2.size()) {
- return true;
- }
- for (int i = 0; i < list1.size(); i++) {
- if (!remoteActionsMatch(list1.get(i), list2.get(i))) {
- return true;
- }
- }
- return false;
- }
-
- /** @return {@link TaskSnapshot} for a given task id. */
- @Nullable
- public static TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
- if (taskId <= 0) return null;
- try {
- return ActivityTaskManager.getService().getTaskSnapshot(
- taskId, isLowResolution, false /* takeSnapshotIfNeeded */);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get task snapshot, taskId=" + taskId, e);
- return null;
- }
- }
-
- public static boolean isPip2ExperimentEnabled() {
- return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 5c65d78..ddea574 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -77,6 +77,7 @@
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.IPip;
@@ -93,7 +94,6 @@
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.KeyguardChangeListener;
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 837426a..fc34772 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
@@ -67,7 +67,7 @@
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipUiEventLogger;
-import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
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 6055fd9..cf54a71 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
@@ -51,13 +51,13 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
index f1606f6..6b890c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
@@ -36,7 +36,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.pip.PipMediaController;
-import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 7c15637..57439a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -55,7 +55,7 @@
import com.android.internal.widget.RecyclerView;
import com.android.wm.shell.R;
import com.android.wm.shell.common.TvWindowMenuActionButton;
-import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.List;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 39c7a4b..1c94625 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -37,8 +37,8 @@
import com.android.internal.util.ImageUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.List;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index dbec607..6720804 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -26,6 +26,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -36,7 +37,6 @@
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.Objects;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
new file mode 100644
index 0000000..ec09827
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
@@ -0,0 +1,3 @@
+# WM shell sub-module pip owner
+hwwang@google.com
+mateuszc@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 7df658e..d310ae3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -37,8 +37,12 @@
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
@@ -334,6 +338,10 @@
boolean isDisplayRotationAnimationStarted = false;
final boolean isDreamTransition = isDreamTransition(info);
final boolean isOnlyTranslucent = isOnlyTranslucent(info);
+ final boolean isActivityReplace = checkActivityReplacement(info, startTransaction);
+ // Some patterns (eg. activity "replacement") require us to re-interpret the type
+ @WindowManager.TransitionType final int transitType =
+ isActivityReplace ? TRANSIT_OPEN : info.getType();
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -430,7 +438,8 @@
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
- Animation a = loadAnimation(info, change, wallpaperTransit, isDreamTransition);
+ Animation a = loadAnimation(transitType, info, change, wallpaperTransit,
+ isDreamTransition);
if (a != null) {
if (isTask) {
final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0;
@@ -604,6 +613,53 @@
return (translucentOpen + translucentClose) > 0;
}
+ /**
+ * Checks for an edge-case where an activity calls finish() followed immediately by
+ * startActivity() to "replace" itself. If in this case, it will swap the layer of the
+ * close/open activities and return `true`. This way, we pretend like we are just "opening"
+ * the new activity.
+ */
+ private static boolean checkActivityReplacement(@NonNull TransitionInfo info,
+ SurfaceControl.Transaction t) {
+ if (info.getType() != TRANSIT_CLOSE) {
+ return false;
+ }
+ int closing = -1;
+ int opening = -1;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if ((change.getTaskInfo() != null || change.hasFlags(FLAG_IS_DISPLAY))
+ && !TransitionUtil.isOrderOnly(change)) {
+ // This isn't an activity-level transition.
+ return false;
+ }
+ if (change.getTaskInfo() != null
+ && change.hasFlags(FLAG_IS_DISPLAY | FLAGS_IS_NON_APP_WINDOW)) {
+ // Ignore non-activity containers.
+ continue;
+ }
+ if (TransitionUtil.isClosingType(change.getMode())) {
+ closing = i;
+ } else if (change.getMode() == TRANSIT_OPEN) {
+ // OPEN implies that it is a new launch. If going "back" the opening app will be
+ // TO_FRONT
+ opening = i;
+ } else if (change.getMode() == TRANSIT_TO_FRONT) {
+ // Normal "going back", so not a replacement.
+ return false;
+ }
+ }
+ if (closing < 0 || opening < 0) {
+ return false;
+ }
+ // Swap the opening and closing z-orders since we're swapping the transit type.
+ final int numChanges = info.getChanges().size();
+ final int zSplitLine = numChanges + 1;
+ t.setLayer(info.getChanges().get(opening).getLeash(), zSplitLine + numChanges - opening);
+ t.setLayer(info.getChanges().get(closing).getLeash(), zSplitLine - closing);
+ return true;
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@@ -656,12 +712,11 @@
}
@Nullable
- private Animation loadAnimation(@NonNull TransitionInfo info,
+ private Animation loadAnimation(int type, @NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, int wallpaperTransit,
boolean isDreamTransition) {
Animation a;
- final int type = info.getType();
final int flags = info.getFlags();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
@@ -716,8 +771,8 @@
// If there's a scene-transition, then jump-cut.
return null;
} else {
- a = loadAttributeAnimation(
- info, change, wallpaperTransit, mTransitionAnimation, isDreamTransition);
+ a = loadAttributeAnimation(type, info, change, wallpaperTransit, mTransitionAnimation,
+ isDreamTransition);
}
if (a != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index d07d2b7b6..c99911d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -45,6 +45,7 @@
import android.graphics.Shader;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.window.ScreenCapture;
@@ -61,10 +62,10 @@
/** Loads the animation that is defined through attribute id for the given transition. */
@Nullable
- public static Animation loadAttributeAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, int wallpaperTransit,
- @NonNull TransitionAnimation transitionAnimation, boolean isDreamTransition) {
- final int type = info.getType();
+ public static Animation loadAttributeAnimation(@WindowManager.TransitionType int type,
+ @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
+ int wallpaperTransit, @NonNull TransitionAnimation transitionAnimation,
+ boolean isDreamTransition) {
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
final boolean enter = TransitionUtil.isOpeningType(changeMode);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
index 367676f..f7a060f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
@@ -46,5 +46,7 @@
default void onStateChangeProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
default void onStateChangeFinished() {}
+
+ default void onFoldStateChanged(boolean isFolded) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 2eb6e71..68b5a81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -63,6 +63,7 @@
@Nullable
private IBinder mTransition;
+ private boolean mAnimationFinished = false;
private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>();
public UnfoldTransitionHandler(ShellInit shellInit,
@@ -132,6 +133,13 @@
startTransaction.apply();
mFinishCallback = finishCallback;
+
+ // Shell transition started when unfold animation has already finished,
+ // finish shell transition immediately
+ if (mAnimationFinished) {
+ finishTransitionIfNeeded();
+ }
+
return true;
}
@@ -161,17 +169,8 @@
@Override
public void onStateChangeFinished() {
- if (mFinishCallback == null) return;
-
- for (int i = 0; i < mAnimators.size(); i++) {
- final UnfoldTaskAnimator animator = mAnimators.get(i);
- animator.clearTasks();
- animator.stop();
- }
-
- mFinishCallback.onTransitionFinished(null);
- mFinishCallback = null;
- mTransition = null;
+ mAnimationFinished = true;
+ finishTransitionIfNeeded();
}
@Override
@@ -218,4 +217,25 @@
public boolean willHandleTransition() {
return mTransition != null;
}
+
+ @Override
+ public void onFoldStateChanged(boolean isFolded) {
+ if (isFolded) {
+ mAnimationFinished = false;
+ }
+ }
+
+ private void finishTransitionIfNeeded() {
+ if (mFinishCallback == null) return;
+
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ animator.clearTasks();
+ animator.stop();
+ }
+
+ mFinishCallback.onTransitionFinished(null);
+ mFinishCallback = null;
+ mTransition = null;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index 97147a3..bc095bb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -31,6 +31,7 @@
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.LaunchBubbleHelper
+import com.android.server.wm.flicker.helpers.MultiWindowUtils
import com.android.wm.shell.flicker.BaseTest
import org.junit.runners.Parameterized
@@ -56,6 +57,9 @@
): FlickerBuilder.() -> Unit {
return {
setup {
+ MultiWindowUtils.executeShellCommand(
+ instrumentation,
+ "settings put secure force_hide_bubbles_user_education 1")
notifyManager.setBubblesAllowed(
testApp.packageName,
uid,
@@ -67,6 +71,9 @@
}
teardown {
+ MultiWindowUtils.executeShellCommand(
+ instrumentation,
+ "settings put secure force_hide_bubbles_user_education 0")
notifyManager.setBubblesAllowed(
testApp.packageName,
uid,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
index 139724f..58d9a64 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
@@ -203,6 +203,60 @@
assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
}
+ /** Test that the default resting position on tablet is middle right. */
+ @Test
+ public void testGetDefaultPosition_appBubble_onTablet() {
+ new WindowManagerConfig().setLargeScreen().setUpConfig();
+ mPositioner.update();
+
+ RectF allowableStackRegion =
+ mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
+ PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
+
+ assertThat(startPosition.x).isEqualTo(allowableStackRegion.right);
+ assertThat(startPosition.y).isEqualTo(getDefaultYPosition());
+ }
+
+ @Test
+ public void testGetRestingPosition_appBubble_onTablet_RTL() {
+ new WindowManagerConfig().setLargeScreen().setLayoutDirection(
+ LAYOUT_DIRECTION_RTL).setUpConfig();
+ mPositioner.update();
+
+ RectF allowableStackRegion =
+ mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
+ PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
+
+ assertThat(startPosition.x).isEqualTo(allowableStackRegion.left);
+ assertThat(startPosition.y).isEqualTo(getDefaultYPosition());
+ }
+
+ @Test
+ public void testHasUserModifiedDefaultPosition_false() {
+ new WindowManagerConfig().setLargeScreen().setLayoutDirection(
+ LAYOUT_DIRECTION_RTL).setUpConfig();
+ mPositioner.update();
+
+ assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
+
+ mPositioner.setRestingPosition(mPositioner.getDefaultStartPosition());
+
+ assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
+ }
+
+ @Test
+ public void testHasUserModifiedDefaultPosition_true() {
+ new WindowManagerConfig().setLargeScreen().setLayoutDirection(
+ LAYOUT_DIRECTION_RTL).setUpConfig();
+ mPositioner.update();
+
+ assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
+
+ mPositioner.setRestingPosition(new PointF(0, 100));
+
+ assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue();
+ }
+
/**
* Calculates the Y position bubbles should be placed based on the config. Based on
* the calculations in {@link BubblePositioner#getDefaultStartPosition()} and
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index efc69ebd..9b9600e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -41,6 +41,7 @@
import android.testing.AndroidTestingRunner;
import android.view.InsetsSource;
import android.view.InsetsState;
+import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
@@ -113,6 +114,9 @@
@Mock
private CompatUIShellCommandHandler mCompatUIShellCommandHandler;
+ @Mock
+ private AccessibilityManager mAccessibilityManager;
+
@Captor
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -139,7 +143,7 @@
mController = new CompatUIController(mContext, mShellInit, mMockShellController,
mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
- mCompatUIConfiguration, mCompatUIShellCommandHandler) {
+ mCompatUIConfiguration, mCompatUIShellCommandHandler, mAccessibilityManager) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
index a802f15a..5867a85 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
@@ -109,6 +109,6 @@
private ReachabilityEduWindowManager createReachabilityEduWindowManager(TaskInfo taskInfo) {
return new ReachabilityEduWindowManager(mContext, taskInfo, mSyncTransactionQueue,
mTaskListener, mDisplayLayout, mCompatUIConfiguration, mExecutor,
- mOnDismissCallback);
+ mOnDismissCallback, flags -> 0);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
index 1fee153..ce1290b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
@@ -93,7 +93,7 @@
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
mSyncTransactionQueue, mTaskListener, new DisplayLayout(),
new CompatUIController.CompatUIHintsState(),
- mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor());
+ mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor(), flags -> 0);
mLayout = (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate(
R.layout.user_aspect_ratio_settings_layout, null);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index b48538c..08cc2f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -106,7 +106,7 @@
false, /* topActivityBoundsLetterboxed */ true);
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
- mOnUserAspectRatioSettingsButtonClicked, mExecutor);
+ mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0);
spyOn(mWindowManager);
doReturn(mLayout).when(mWindowManager).inflateLayout();
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 99a1ac6..a57a7bf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -99,6 +99,7 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -1061,7 +1062,8 @@
mTransactionPool, createTestDisplayController(), mMainExecutor,
mMainHandler, mAnimExecutor);
final RecentsTransitionHandler recentsHandler =
- new RecentsTransitionHandler(shellInit, transitions, null);
+ new RecentsTransitionHandler(shellInit, transitions,
+ mock(RecentTasksController.class));
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
shellInit.init();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
new file mode 100644
index 0000000..63a685e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.unfold;
+
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.Display;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
+import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
+import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class UnfoldTransitionHandlerTest {
+
+ private UnfoldTransitionHandler mUnfoldTransitionHandler;
+
+ private final TestShellUnfoldProgressProvider mShellUnfoldProgressProvider =
+ new TestShellUnfoldProgressProvider();
+ private final TestTransactionPool mTransactionPool = new TestTransactionPool();
+
+ private FullscreenUnfoldTaskAnimator mFullscreenUnfoldTaskAnimator;
+ private SplitTaskUnfoldAnimator mSplitTaskUnfoldAnimator;
+ private Transitions mTransitions;
+
+ private final IBinder mTransition = new Binder();
+
+ @Before
+ public void before() {
+ final ShellExecutor executor = new TestSyncExecutor();
+ final ShellInit shellInit = new ShellInit(executor);
+
+ mFullscreenUnfoldTaskAnimator = mock(FullscreenUnfoldTaskAnimator.class);
+ mSplitTaskUnfoldAnimator = mock(SplitTaskUnfoldAnimator.class);
+ mTransitions = mock(Transitions.class);
+
+ mUnfoldTransitionHandler = new UnfoldTransitionHandler(
+ shellInit,
+ mShellUnfoldProgressProvider,
+ mFullscreenUnfoldTaskAnimator,
+ mSplitTaskUnfoldAnimator,
+ mTransactionPool,
+ executor,
+ mTransitions
+ );
+
+ shellInit.init();
+ }
+
+ @Test
+ public void handleRequest_physicalDisplayChange_handlesTransition() {
+ ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
+ TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
+ Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true);
+ TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE,
+ triggerTaskInfo, /* remoteTransition= */ null, displayChange);
+
+ WindowContainerTransaction result = mUnfoldTransitionHandler.handleRequest(mTransition,
+ requestInfo);
+
+ assertThat(result).isNotNull();
+ }
+
+ @Test
+ public void handleRequest_noPhysicalDisplayChange_doesNotHandleTransition() {
+ ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
+ TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
+ Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(false);
+ TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE,
+ triggerTaskInfo, /* remoteTransition= */ null, displayChange);
+
+ WindowContainerTransaction result = mUnfoldTransitionHandler.handleRequest(mTransition,
+ requestInfo);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() {
+ TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+ mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+ TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+ mUnfoldTransitionHandler.startAnimation(
+ mTransition,
+ mock(TransitionInfo.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ finishCallback
+ );
+
+ verify(finishCallback, never()).onTransitionFinished(any());
+ }
+
+ @Test
+ public void startAnimation_animationFinishes_finishesTheTransition() {
+ TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+ mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+ TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+ mUnfoldTransitionHandler.startAnimation(
+ mTransition,
+ mock(TransitionInfo.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ finishCallback
+ );
+ mShellUnfoldProgressProvider.onStateChangeStarted();
+ mShellUnfoldProgressProvider.onStateChangeFinished();
+
+ verify(finishCallback).onTransitionFinished(any());
+ }
+
+ @Test
+ public void startAnimation_animationIsAlreadyFinished_finishesTheTransition() {
+ TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+ mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+ TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+ mShellUnfoldProgressProvider.onStateChangeStarted();
+ mShellUnfoldProgressProvider.onStateChangeFinished();
+ mUnfoldTransitionHandler.startAnimation(
+ mTransition,
+ mock(TransitionInfo.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ finishCallback
+ );
+
+ verify(finishCallback).onTransitionFinished(any());
+ }
+
+ @Test
+ public void startAnimationSecondTimeAfterFold_animationAlreadyFinished_finishesTransition() {
+ TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+ TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+ // First unfold
+ mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ false);
+ mShellUnfoldProgressProvider.onStateChangeStarted();
+ mShellUnfoldProgressProvider.onStateChangeFinished();
+ mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+ mUnfoldTransitionHandler.startAnimation(
+ mTransition,
+ mock(TransitionInfo.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ finishCallback
+ );
+ clearInvocations(finishCallback);
+
+ // Fold
+ mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ true);
+
+ // Second unfold
+ mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ false);
+ mShellUnfoldProgressProvider.onStateChangeStarted();
+ mShellUnfoldProgressProvider.onStateChangeFinished();
+ mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+ mUnfoldTransitionHandler.startAnimation(
+ mTransition,
+ mock(TransitionInfo.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ finishCallback
+ );
+
+ verify(finishCallback).onTransitionFinished(any());
+ }
+
+ private TransitionRequestInfo createUnfoldTransitionRequestInfo() {
+ ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
+ TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
+ Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true);
+ return new TransitionRequestInfo(TRANSIT_CHANGE,
+ triggerTaskInfo, /* remoteTransition= */ null, displayChange);
+ }
+
+ private static class TestShellUnfoldProgressProvider implements ShellUnfoldProgressProvider,
+ ShellUnfoldProgressProvider.UnfoldListener {
+
+ private final List<UnfoldListener> mListeners = new ArrayList<>();
+
+ @Override
+ public void addListener(Executor executor, UnfoldListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void onFoldStateChanged(boolean isFolded) {
+ mListeners.forEach(unfoldListener -> unfoldListener.onFoldStateChanged(isFolded));
+ }
+
+ @Override
+ public void onStateChangeFinished() {
+ mListeners.forEach(UnfoldListener::onStateChangeFinished);
+ }
+
+ @Override
+ public void onStateChangeProgress(float progress) {
+ mListeners.forEach(unfoldListener -> unfoldListener.onStateChangeProgress(progress));
+ }
+
+ @Override
+ public void onStateChangeStarted() {
+ mListeners.forEach(UnfoldListener::onStateChangeStarted);
+ }
+ }
+
+ private static class TestTransactionPool extends TransactionPool {
+ @Override
+ public SurfaceControl.Transaction acquire() {
+ return mock(SurfaceControl.Transaction.class);
+ }
+
+ @Override
+ public void release(SurfaceControl.Transaction t) {
+ }
+ }
+
+ private static class TestSyncExecutor implements ShellExecutor {
+ @Override
+ public void execute(Runnable runnable) {
+ runnable.run();
+ }
+
+ @Override
+ public void executeDelayed(Runnable runnable, long delayMillis) {
+ runnable.run();
+ }
+
+ @Override
+ public void removeCallbacks(Runnable runnable) {
+ }
+
+ @Override
+ public boolean hasCallback(Runnable runnable) {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/location/Android.bp b/location/Android.bp
new file mode 100644
index 0000000..46dca74
--- /dev/null
+++ b/location/Android.bp
@@ -0,0 +1,41 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+//location sources that will populate the new module
+filegroup {
+ name: "framework-location-nonupdatable-sources",
+ srcs: [
+ "placeholder_java/android/location/Placeholder.java",
+ ],
+}
+
+java_sdk_library {
+ name: "framework-location",
+ srcs: [
+ ":framework-location-nonupdatable-sources",
+ ],
+ defaults: ["framework-non-updatable-unbundled-defaults"],
+ permitted_packages: [
+ "android.location",
+ "com.android.internal.location",
+ ],
+ libs: [
+ "app-compat-annotations",
+ "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+ ],
+ hidden_api_packages: [
+ "com.android.internal.location",
+ ],
+ aidl: {
+ include_dirs: [
+ "frameworks/base/location/java",
+ "frameworks/base/core/java",
+ ],
+ },
+}
diff --git a/location/api/current.txt b/location/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/module-lib-current.txt b/location/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/module-lib-removed.txt b/location/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/removed.txt b/location/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/system-removed.txt b/location/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/test-current.txt b/location/api/test-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/test-removed.txt b/location/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/java/android/location/GnssRequest.java b/location/java/android/location/GnssRequest.java
index 9c9766f..f3a40cc 100644
--- a/location/java/android/location/GnssRequest.java
+++ b/location/java/android/location/GnssRequest.java
@@ -41,7 +41,7 @@
*
* <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
* discontinuities are expected, and when supported, carrier phase should be continuous in
- * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+ * good signal conditions. All non-denylisted, healthy constellations, satellites and
* frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
* is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
* duty cycling, constellations and frequency limits, etc.
@@ -138,7 +138,7 @@
*
* <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
* discontinuities are expected, and when supported, carrier phase should be continuous in
- * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+ * good signal conditions. All non-denylisted, healthy constellations, satellites and
* frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
* is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
* duty cycling, constellations and frequency limits, etc.
diff --git a/location/placeholder_java/android/location/Placeholder.java b/location/placeholder_java/android/location/Placeholder.java
new file mode 100644
index 0000000..f0dbce8
--- /dev/null
+++ b/location/placeholder_java/android/location/Placeholder.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.location;
+
+/**
+ * Placeholder class so new frameworks-location module isn't empty, will be removed once module is
+ * populated.
+ *
+ * @hide
+ *
+ */
+public class Placeholder {
+}
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 5ae77b5..a9da832 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -37,6 +37,17 @@
}
],
"file_patterns": ["(?i)drm|crypto"]
+ },
+ {
+ "file_patterns": [
+ "[^/]*(Ringtone)[^/]*\\.java"
+ ],
+ "name": "MediaRingtoneTests",
+ "options": [
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
}
]
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 0ff1b1e..9234479 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -35,19 +36,20 @@
import android.database.Cursor;
import android.database.StaleDataException;
import android.net.Uri;
-import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.vibrator.persistence.VibrationXmlParser;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.AudioColumns;
import android.provider.MediaStore.MediaColumns;
import android.provider.Settings;
import android.provider.Settings.System;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.database.SortCursor;
@@ -58,6 +60,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -209,21 +213,30 @@
*/
public static final String EXTRA_RINGTONE_PICKED_URI =
"android.intent.extra.ringtone.PICKED_URI";
-
+
+ /**
+ * Declares the allowed types of media for this RingtoneManager.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "MEDIA_", value = {
+ Ringtone.MEDIA_SOUND,
+ Ringtone.MEDIA_VIBRATION,
+ })
+ public @interface MediaType {}
+
// Make sure the column ordering and then ..._COLUMN_INDEX are in sync
- private static final String[] INTERNAL_COLUMNS = new String[] {
+ private static final String[] MEDIA_AUDIO_COLUMNS = new String[] {
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.TITLE_KEY,
};
- private static final String[] MEDIA_COLUMNS = new String[] {
- MediaStore.Audio.Media._ID,
- MediaStore.Audio.Media.TITLE,
- MediaStore.Audio.Media.TITLE,
- MediaStore.Audio.Media.TITLE_KEY,
+ private static final String[] MEDIA_VIBRATION_COLUMNS = new String[]{
+ MediaStore.Files.FileColumns._ID,
+ MediaStore.Files.FileColumns.TITLE,
};
/**
@@ -251,7 +264,9 @@
private Cursor mCursor;
private int mType = TYPE_RINGTONE;
-
+ @MediaType
+ private int mMediaType = Ringtone.MEDIA_SOUND;
+
/**
* If a column (item from this list) exists in the Cursor, its value must
* be true (value of 1) for the row to be returned.
@@ -318,6 +333,41 @@
}
/**
+ * Sets the media type that will be listed by the RingtoneManager.
+ *
+ * <p>This method should be called before calling {@link RingtoneManager#getCursor()}.
+ *
+ * @hide
+ */
+ public void setMediaType(@MediaType int mediaType) {
+ if (mCursor != null) {
+ throw new IllegalStateException(
+ "Setting media should be done before calling getCursor().");
+ }
+
+ switch (mediaType) {
+ case Ringtone.MEDIA_SOUND:
+ case Ringtone.MEDIA_VIBRATION:
+ mMediaType = mediaType;
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported media type " + mediaType);
+ }
+ }
+
+ /**
+ * Returns the RingtoneManagers media type.
+ *
+ * @return the media type.
+ * @see #setMediaType
+ * @hide
+ */
+ @MediaType
+ public int getMediaType() {
+ return mMediaType;
+ }
+
+ /**
* Sets which type(s) of ringtones will be listed by this.
*
* @param type The type(s), one or more of {@link #TYPE_RINGTONE},
@@ -454,19 +504,19 @@
return mCursor;
}
- ArrayList<Cursor> ringtoneCursors = new ArrayList<Cursor>();
- ringtoneCursors.add(getInternalRingtones());
- ringtoneCursors.add(getMediaRingtones());
+ ArrayList<Cursor> cursors = new ArrayList<>();
+
+ cursors.add(queryMediaStore(/* internal= */ true));
+ cursors.add(queryMediaStore(/* internal= */ false));
if (mIncludeParentRingtones) {
Cursor parentRingtonesCursor = getParentProfileRingtones();
if (parentRingtonesCursor != null) {
- ringtoneCursors.add(parentRingtonesCursor);
+ cursors.add(parentRingtonesCursor);
}
}
-
- return mCursor = new SortCursor(ringtoneCursors.toArray(new Cursor[ringtoneCursors.size()]),
- MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
+ return mCursor = new SortCursor(cursors.toArray(new Cursor[cursors.size()]),
+ getSortOrderForMedia(mMediaType));
}
private Cursor getParentProfileRingtones() {
@@ -478,9 +528,7 @@
// We don't need to re-add the internal ringtones for the work profile since
// they are the same as the personal profile. We just need the external
// ringtones.
- final Cursor res = getMediaRingtones(parentContext);
- return new ExternalRingtonesCursorWrapper(res, ContentProvider.maybeAddUserId(
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, parentInfo.id));
+ return queryMediaStore(parentContext, /* internal= */ false);
}
}
return null;
@@ -502,7 +550,7 @@
Uri positionUri = getRingtoneUri(position);
if (Ringtone.useRingtoneV2()) {
mPreviousRingtone = new Ringtone.Builder(
- mContext, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(mType))
+ mContext, mMediaType, getDefaultAudioAttributes(mType))
.setUri(positionUri)
.build();
} else {
@@ -675,11 +723,13 @@
*/
public static Uri getValidRingtoneUri(Context context) {
final RingtoneManager rm = new RingtoneManager(context);
-
- Uri uri = getValidRingtoneUriFromCursorAndClose(context, rm.getInternalRingtones());
+
+ Uri uri = getValidRingtoneUriFromCursorAndClose(context,
+ rm.queryMediaStore(/* internal= */ true));
if (uri == null) {
- uri = getValidRingtoneUriFromCursorAndClose(context, rm.getMediaRingtones());
+ uri = getValidRingtoneUriFromCursorAndClose(context,
+ rm.queryMediaStore(/* internal= */ false));
}
return uri;
@@ -700,28 +750,26 @@
}
}
- @UnsupportedAppUsage
- private Cursor getInternalRingtones() {
- final Cursor res = query(
- MediaStore.Audio.Media.INTERNAL_CONTENT_URI, INTERNAL_COLUMNS,
- constructBooleanTrueWhereClause(mFilterColumns),
- null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
- return new ExternalRingtonesCursorWrapper(res, MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
+ private Cursor queryMediaStore(boolean internal) {
+ return queryMediaStore(mContext, internal);
}
- private Cursor getMediaRingtones() {
- final Cursor res = getMediaRingtones(mContext);
- return new ExternalRingtonesCursorWrapper(res, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
- }
+ private Cursor queryMediaStore(Context context, boolean internal) {
+ Uri contentUri = getContentUriForMedia(mMediaType, internal);
+ String[] columns =
+ mMediaType == Ringtone.MEDIA_VIBRATION ? MEDIA_VIBRATION_COLUMNS
+ : MEDIA_AUDIO_COLUMNS;
+ String whereClause = getWhereClauseForMedia(mMediaType, mFilterColumns);
+ String sortOrder = getSortOrderForMedia(mMediaType);
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private Cursor getMediaRingtones(Context context) {
- // MediaStore now returns ringtones on other storage devices, even when
- // we don't have storage or audio permissions
- return query(
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
- constructBooleanTrueWhereClause(mFilterColumns), null,
- MediaStore.Audio.Media.DEFAULT_SORT_ORDER, context);
+ Cursor cursor = query(contentUri, columns, whereClause, /* selectionArgs= */ null,
+ sortOrder, context);
+
+ if (context.getUserId() != mContext.getUserId()) {
+ contentUri = ContentProvider.maybeAddUserId(contentUri, context.getUserId());
+ }
+
+ return new ExternalRingtonesCursorWrapper(cursor, contentUri);
}
private void setFilterColumnsList(int type) {
@@ -740,6 +788,56 @@
columns.add(MediaStore.Audio.AudioColumns.IS_ALARM);
}
}
+
+ /**
+ * Returns the sort order for the specified media.
+ *
+ * @param media The RingtoneManager media type.
+ * @return The sort order column.
+ */
+ private static String getSortOrderForMedia(@MediaType int media) {
+ return media == Ringtone.MEDIA_VIBRATION ? MediaStore.Files.FileColumns.TITLE
+ : MediaStore.Audio.Media.DEFAULT_SORT_ORDER;
+ }
+
+ /**
+ * Returns the content URI based on the specified media and whether it's internal or external
+ * storage.
+ *
+ * @param media The RingtoneManager media type.
+ * @param internal Whether it's for internal or external storage.
+ * @return The media content URI.
+ */
+ private static Uri getContentUriForMedia(@MediaType int media, boolean internal) {
+ switch (media) {
+ case Ringtone.MEDIA_VIBRATION:
+ return MediaStore.Files.getContentUri(
+ internal ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL);
+ case Ringtone.MEDIA_SOUND:
+ return internal ? MediaStore.Audio.Media.INTERNAL_CONTENT_URI
+ : MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ default:
+ throw new IllegalArgumentException("Unsupported media type " + media);
+ }
+ }
+
+ /**
+ * Constructs a where clause based on the media type. This will be used to find all matching
+ * sound or vibration files.
+ *
+ * @param media The RingtoneManager media type.
+ * @param columns The columns that must be true, when media type is {@link Ringtone#MEDIA_SOUND}
+ * @return The where clause.
+ */
+ private static String getWhereClauseForMedia(@MediaType int media, List<String> columns) {
+ // TODO(b/296213309): Filtering by ringtone-type isn't supported yet for vibrations.
+ if (media == Ringtone.MEDIA_VIBRATION) {
+ return TextUtils.formatSimple("(%s='%s')", MediaStore.Files.FileColumns.MIME_TYPE,
+ VibrationXmlParser.APPLICATION_VIBRATION_XML_MIME_TYPE);
+ }
+
+ return constructBooleanTrueWhereClause(columns);
+ }
/**
* Constructs a where clause that consists of at least one column being 1
@@ -769,14 +867,6 @@
return sb.toString();
}
-
- private Cursor query(Uri uri,
- String[] projection,
- String selection,
- String[] selectionArgs,
- String sortOrder) {
- return query(uri, projection, selection, selectionArgs, sortOrder, mContext);
- }
private Cursor query(Uri uri,
String[] projection,
diff --git a/media/tests/ringtone/Android.bp b/media/tests/ringtone/Android.bp
new file mode 100644
index 0000000..55b98c4
--- /dev/null
+++ b/media/tests/ringtone/Android.bp
@@ -0,0 +1,30 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "MediaRingtoneTests",
+
+ srcs: ["src/**/*.java"],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ static_libs: [
+ "androidx.test.rules",
+ "testng",
+ "androidx.test.ext.truth",
+ "frameworks-base-testutils",
+ ],
+
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
+
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/media/tests/ringtone/AndroidManifest.xml b/media/tests/ringtone/AndroidManifest.xml
new file mode 100644
index 0000000..27eda07
--- /dev/null
+++ b/media/tests/ringtone/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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.framework.base.media.ringtone.tests">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name="MediaRingtoneTests"
+ android:label="Media Ringtone Tests"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.framework.base.media.ringtone.tests"
+ android:label="Media Ringtone Tests"/>
+</manifest>
diff --git a/media/tests/ringtone/TEST_MAPPING b/media/tests/ringtone/TEST_MAPPING
new file mode 100644
index 0000000..6f25c14
--- /dev/null
+++ b/media/tests/ringtone/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+ "presubmit": [
+ {
+ "name": "MediaRingtoneTests",
+ "options": [
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "MediaRingtoneTests",
+ "options": [
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/media/tests/ringtone/res/raw/test_haptic_file.ahv b/media/tests/ringtone/res/raw/test_haptic_file.ahv
new file mode 100644
index 0000000..18c99c7
--- /dev/null
+++ b/media/tests/ringtone/res/raw/test_haptic_file.ahv
@@ -0,0 +1,17 @@
+<vibration>
+ <waveform-effect>
+ <waveform-entry durationMs="63" amplitude="255"/>
+ <waveform-entry durationMs="63" amplitude="231"/>
+ <waveform-entry durationMs="63" amplitude="208"/>
+ <waveform-entry durationMs="63" amplitude="185"/>
+ <waveform-entry durationMs="63" amplitude="162"/>
+ <waveform-entry durationMs="63" amplitude="139"/>
+ <waveform-entry durationMs="63" amplitude="115"/>
+ <waveform-entry durationMs="63" amplitude="92"/>
+ <waveform-entry durationMs="63" amplitude="69"/>
+ <waveform-entry durationMs="63" amplitude="46"/>
+ <waveform-entry durationMs="63" amplitude="23"/>
+ <waveform-entry durationMs="63" amplitude="0"/>
+ <waveform-entry durationMs="1250" amplitude="0"/>
+ </waveform-effect>
+</vibration>
diff --git a/media/tests/ringtone/res/raw/test_sound_file.mp3 b/media/tests/ringtone/res/raw/test_sound_file.mp3
new file mode 100644
index 0000000..c1b2fdf
--- /dev/null
+++ b/media/tests/ringtone/res/raw/test_sound_file.mp3
Binary files differ
diff --git a/media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java b/media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java
new file mode 100644
index 0000000..a92b298
--- /dev/null
+++ b/media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media;
+
+import static com.google.android.mms.ContentType.AUDIO_MP3;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.vibrator.persistence.VibrationXmlParser;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.framework.base.media.ringtone.tests.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class RingtoneManagerTest {
+ @RingtoneManager.MediaType
+ private final int mMediaType;
+ private final List<Uri> mAddedFilesUri;
+ private Context mContext;
+ private RingtoneManager mRingtoneManager;
+ private long mTimestamp;
+
+ @Parameterized.Parameters(name = "media = {0}")
+ public static Iterable<?> data() {
+ return Arrays.asList(Ringtone.MEDIA_SOUND, Ringtone.MEDIA_VIBRATION);
+ }
+
+ public RingtoneManagerTest(@RingtoneManager.MediaType int mediaType) {
+ mMediaType = mediaType;
+ mAddedFilesUri = new ArrayList<>();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mTimestamp = SystemClock.uptimeMillis();
+ mRingtoneManager = new RingtoneManager(mContext);
+ mRingtoneManager.setMediaType(mMediaType);
+ }
+
+ @After
+ public void tearDown() {
+ // Clean up media store
+ for (Uri fileUri : mAddedFilesUri) {
+ mContext.getContentResolver().delete(fileUri, null);
+ }
+ }
+
+ @Test
+ public void testSetMediaType_withValidValue_setsMediaCorrectly() {
+ mRingtoneManager.setMediaType(mMediaType);
+ assertThat(mRingtoneManager.getMediaType()).isEqualTo(mMediaType);
+ }
+
+ @Test
+ public void testSetMediaType_withInvalidValue_throwsException() {
+ assertThrows(IllegalArgumentException.class, () -> mRingtoneManager.setMediaType(999));
+ }
+
+ @Test
+ public void testSetMediaType_afterCallingGetCursor_throwsException() {
+ mRingtoneManager.getCursor();
+ assertThrows(IllegalStateException.class, () -> mRingtoneManager.setMediaType(mMediaType));
+ }
+
+ @Test
+ public void testGetRingtone_ringtoneHasCorrectTitle() throws Exception {
+ String fileName = generateUniqueFileName("new_file");
+ Ringtone ringtone = addNewRingtoneToMediaStore(mRingtoneManager, fileName);
+
+ assertThat(ringtone.getTitle(mContext)).isEqualTo(fileName);
+ }
+
+ @Test
+ public void testGetRingtone_ringtoneCanBePlayedAndStopped() throws Exception {
+ //TODO(b/261571543) Remove this assumption once we support playing vibrations.
+ assumeTrue(mMediaType == Ringtone.MEDIA_SOUND);
+ String fileName = generateUniqueFileName("new_file");
+ Ringtone ringtone = addNewRingtoneToMediaStore(mRingtoneManager, fileName);
+
+ ringtone.play();
+ assertThat(ringtone.isPlaying()).isTrue();
+
+ ringtone.stop();
+ assertThat(ringtone.isPlaying()).isFalse();
+ }
+
+ @Test
+ public void testGetCursor_withDifferentMedia_returnsCorrectCursor() throws Exception {
+ RingtoneManager audioRingtoneManager = new RingtoneManager(mContext);
+ String audioFileName = generateUniqueFileName("ringtone");
+ addNewRingtoneToMediaStore(audioRingtoneManager, audioFileName);
+
+ RingtoneManager vibrationRingtoneManager = new RingtoneManager(mContext);
+ vibrationRingtoneManager.setMediaType(Ringtone.MEDIA_VIBRATION);
+ String vibrationFileName = generateUniqueFileName("vibration");
+ addNewRingtoneToMediaStore(vibrationRingtoneManager, vibrationFileName);
+
+ Cursor audioCursor = audioRingtoneManager.getCursor();
+ Cursor vibrationCursor = vibrationRingtoneManager.getCursor();
+
+ List<String> audioTitles = extractRecordTitles(audioCursor);
+ List<String> vibrationTitles = extractRecordTitles(vibrationCursor);
+
+ assertThat(audioTitles).contains(audioFileName);
+ assertThat(audioTitles).doesNotContain(vibrationFileName);
+
+ assertThat(vibrationTitles).contains(vibrationFileName);
+ assertThat(vibrationTitles).doesNotContain(audioFileName);
+ }
+
+ private List<String> extractRecordTitles(Cursor cursor) {
+ List<String> titles = new ArrayList<>();
+
+ if (cursor.moveToFirst()) {
+ do {
+ String title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
+ titles.add(title);
+ } while (cursor.moveToNext());
+ }
+
+ return titles;
+ }
+
+ private Ringtone addNewRingtoneToMediaStore(RingtoneManager ringtoneManager, String fileName)
+ throws Exception {
+ Uri fileUri = ringtoneManager.getMediaType() == Ringtone.MEDIA_SOUND ? addAudioFile(
+ fileName) : addVibrationFile(fileName);
+ mAddedFilesUri.add(fileUri);
+
+ int ringtonePosition = ringtoneManager.getRingtonePosition(fileUri);
+ Ringtone ringtone = ringtoneManager.getRingtone(ringtonePosition);
+ // Validate this is the expected ringtone.
+ assertThat(ringtone.getUri()).isEqualTo(fileUri);
+ return ringtone;
+ }
+
+ private Uri addAudioFile(String fileName) throws Exception {
+ ContentResolver resolver = mContext.getContentResolver();
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(MediaStore.Audio.Media.DISPLAY_NAME, fileName + ".mp3");
+ contentValues.put(MediaStore.Audio.Media.RELATIVE_PATH, Environment.DIRECTORY_RINGTONES);
+ contentValues.put(MediaStore.Audio.Media.MIME_TYPE, AUDIO_MP3);
+ contentValues.put(MediaStore.Audio.Media.TITLE, fileName);
+ contentValues.put(MediaStore.Audio.Media.IS_RINGTONE, 1);
+
+ Uri contentUri = resolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ contentValues);
+ writeRawDataToFile(resolver, contentUri, R.raw.test_sound_file);
+
+ return resolver.canonicalizeOrElse(contentUri);
+ }
+
+ private Uri addVibrationFile(String fileName) throws Exception {
+ ContentResolver resolver = mContext.getContentResolver();
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, fileName + ".ahv");
+ contentValues.put(MediaStore.Files.FileColumns.RELATIVE_PATH,
+ Environment.DIRECTORY_DOWNLOADS);
+ contentValues.put(MediaStore.Files.FileColumns.MIME_TYPE,
+ VibrationXmlParser.APPLICATION_VIBRATION_XML_MIME_TYPE);
+ contentValues.put(MediaStore.Files.FileColumns.TITLE, fileName);
+
+ Uri contentUri = resolver.insert(MediaStore.Files.getContentUri(MediaStore
+ .VOLUME_EXTERNAL), contentValues);
+ writeRawDataToFile(resolver, contentUri, R.raw.test_haptic_file);
+
+ return resolver.canonicalizeOrElse(contentUri);
+ }
+
+ private void writeRawDataToFile(ContentResolver resolver, Uri contentUri, int rawResource)
+ throws Exception {
+ try (ParcelFileDescriptor pfd =
+ resolver.openFileDescriptor(contentUri, "w", null)) {
+ InputStream inputStream = mContext.getResources().openRawResource(rawResource);
+ FileOutputStream outputStream = new FileOutputStream(pfd.getFileDescriptor());
+ outputStream.write(inputStream.readAllBytes());
+
+ inputStream.close();
+ outputStream.flush();
+ outputStream.close();
+
+ } catch (Exception e) {
+ throw new Exception("Failed to write data to file", e);
+ }
+ }
+
+ private String generateUniqueFileName(String prefix) {
+ return TextUtils.formatSimple("%s_%d", prefix, mTimestamp);
+ }
+
+}
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index c571b74..c25df6e 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -144,8 +144,6 @@
binder::Status ret =
mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session);
if (!ret.isOk() || !session) {
- ALOGE("%s: PerformanceHint cannot create hint session. %s", __FUNCTION__,
- ret.exceptionMessage().c_str());
return nullptr;
}
return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
diff --git a/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
index 5fa6137..562f7be 100644
--- a/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
+++ b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
@@ -293,7 +293,7 @@
return correct
}
- override fun onDraw(canvas: Canvas?) {
+ override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (!showText) return
canvas?.let {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index 8e0cf89..a428142 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -78,6 +78,7 @@
packageManager.getInstalledModules(0)
.filter { it.isHidden }
.map { it.packageName }
+ .filterNotNull()
.toSet()
}
val hideWhenDisabledPackagesDeferred = async {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
index 69c6131..92fd0cd 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
@@ -82,7 +82,8 @@
val packageInfo = getPackageInfoAsUser(packageName, PackageManager.GET_PERMISSIONS, userId)
val index = packageInfo?.requestedPermissions?.indexOf(permission) ?: return false
return index >= 0 &&
- packageInfo.requestedPermissionsFlags[index].hasFlag(REQUESTED_PERMISSION_GRANTED)
+ checkNotNull(packageInfo.requestedPermissionsFlags)[index]
+ .hasFlag(REQUESTED_PERMISSION_GRANTED)
}
override suspend fun getAppOpPermissionPackages(userId: Int, permission: String): Set<String> =
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
index cc3584b..dfd8f6b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
@@ -28,7 +28,7 @@
}
fun getEnterpriseString(updatableStringId: String, resId: Int): String =
- resources.getString(updatableStringId) { context.getString(resId) }
+ checkNotNull(resources.getString(updatableStringId) { context.getString(resId) })
fun getProfileTitle(isManagedProfile: Boolean): String = if (isManagedProfile) {
getEnterpriseString(WORK_CATEGORY_HEADER, R.string.category_work)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index cee750e..ea83e1d 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -58,7 +58,7 @@
.semantics(mergeDescendants = true) {},
horizontalAlignment = Alignment.CenterHorizontally,
) {
- val app = packageInfo.applicationInfo
+ val app = checkNotNull(packageInfo.applicationInfo)
Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
AppIcon(app = app, size = SettingsDimension.appIconInfoSize)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 7f82be4..62c5f70 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -144,7 +144,7 @@
footerContent = { AnnotatedText(footerResId) },
packageManagers = packageManagers,
) {
- val model = createSwitchModel(applicationInfo)
+ val model = createSwitchModel(checkNotNull(applicationInfo))
val restrictions = Restrictions(userId, switchRestrictionKeys)
RestrictedSwitchPreference(model, restrictions, restrictionsProviderFactory)
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 80cf6c3..20740dc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -223,6 +223,7 @@
VALIDATORS.put(System.NOTIFICATION_LIGHT_PULSE, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.WEAR_TTS_PREWARM_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.UNREAD_NOTIFICATION_DOT_INDICATOR, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.AUTO_LAUNCH_MEDIA_CONTROLS, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c697c1f..203efbf 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -103,6 +103,7 @@
Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
+ Settings.System.WEAR_TTS_PREWARM_ENABLED,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
);
diff --git a/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt b/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt
index 92d752c..4837aad 100644
--- a/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt
+++ b/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt
@@ -88,6 +88,7 @@
} catch (e: Exception) {
return Result.Failure(e)
}
+ checkNotNull(signingInfo)
return if (signingInfo.hasMultipleSigners()) {
signingInfo.apkContentsSigners
} else {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index da48762..0a100ba 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -14,26 +14,20 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.ui.composable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.material3.Button
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
+import android.view.View
+import android.view.ViewGroup
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -42,6 +36,7 @@
import com.android.systemui.scene.ui.composable.ComposableScene
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@@ -54,6 +49,7 @@
constructor(
@Application private val applicationScope: CoroutineScope,
private val viewModel: LockscreenSceneViewModel,
+ @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
) : ComposableScene {
override val key = SceneKey.Lockscreen
@@ -72,6 +68,7 @@
) {
LockscreenScene(
viewModel = viewModel,
+ viewProvider = viewProvider,
modifier = modifier,
)
}
@@ -89,25 +86,22 @@
@Composable
private fun LockscreenScene(
viewModel: LockscreenSceneViewModel,
+ viewProvider: () -> View,
modifier: Modifier = Modifier,
) {
- // TODO(b/280879610): implement the real UI.
-
- val lockButtonIcon: Icon by viewModel.lockButtonIcon.collectAsState()
-
- Box(modifier = modifier) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.align(Alignment.Center)
- ) {
- Text("Lockscreen", style = MaterialTheme.typography.headlineMedium)
- Row(
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- Button(onClick = { viewModel.onLockButtonClicked() }) { Icon(lockButtonIcon) }
-
- Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") }
+ AndroidView(
+ factory = { _ ->
+ val keyguardRootView = viewProvider()
+ // Remove the KeyguardRootView from any parent it might already have in legacy code just
+ // in case (a view can't have two parents).
+ (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
+ keyguardRootView
+ },
+ update = { keyguardRootView ->
+ keyguardRootView.requireViewById<View>(R.id.lock_icon_view).setOnClickListener {
+ viewModel.onLockButtonClicked()
}
- }
- }
+ },
+ modifier = modifier,
+ )
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index ab0225d..966e183 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -51,15 +51,18 @@
private val KEY_TIMESTAMP = "appliedTimestamp"
private val KNOWN_PLUGINS =
mapOf<String, List<ClockMetadata>>(
- "com.android.systemui.falcon.one" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")),
- "com.android.systemui.falcon.two" to listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")),
- "com.android.systemui.falcon.three" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")),
- "com.android.systemui.falcon.four" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")),
- "com.android.systemui.falcon.five" to listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")),
- "com.android.systemui.falcon.six" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")),
- "com.android.systemui.falcon.seven" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")),
- "com.android.systemui.falcon.eight" to listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")),
- "com.android.systemui.falcon.nine" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")),
+ "com.android.systemui.clocks.bignum" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")),
+ "com.android.systemui.clocks.calligraphy" to
+ listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")),
+ "com.android.systemui.clocks.flex" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")),
+ "com.android.systemui.clocks.growth" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")),
+ "com.android.systemui.clocks.handwritten" to
+ listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")),
+ "com.android.systemui.clocks.inflate" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")),
+ "com.android.systemui.clocks.metro" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")),
+ "com.android.systemui.clocks.numoverlap" to
+ listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")),
+ "com.android.systemui.clocks.weather" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")),
)
private fun <TKey : Any, TVal : Any> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 92083b0..785177e 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -292,6 +292,7 @@
-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
-packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
-packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
-packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
diff --git a/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml b/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml
new file mode 100644
index 0000000..a8abd79
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:color="?attr/onShadeActive" android:alpha="0.12" />
+ <item android:state_hovered="true" android:color="?attr/onShadeActive" android:alpha="0.09" />
+ <item android:color="@color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
index a8c0349..47a2965 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
@@ -32,6 +32,12 @@
<corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/qs_footer_power_button_overlay_color"/>
+ <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
+ </shape>
+ </item>
</ripple>
</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml
new file mode 100644
index 0000000..24222f7
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml
@@ -0,0 +1,101 @@
+<!--
+ ~ 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.
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <RelativeLayout
+ android:id="@+id/auth_credential_header"
+ style="?headerStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <ImageView
+ android:id="@+id/icon"
+ style="?headerIconStyle"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@null"/>
+
+ <TextView
+ android:id="@+id/title"
+ style="?titleTextAppearance"
+ android:layout_below="@id/icon"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="?subTitleTextAppearance"
+ android:layout_below="@id/title"
+ android:layout_alignParentLeft="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/description"
+ style="?descriptionTextAppearance"
+ android:layout_below="@id/subtitle"
+ android:layout_alignParentLeft="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </RelativeLayout>
+
+ <FrameLayout
+ android:id="@+id/auth_credential_input"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical">
+
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="?passwordTextAppearance"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp"/>
+
+ <TextView
+ android:id="@+id/error"
+ style="?errorTextAppearance"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/emergencyCallButton"
+ style="@style/AuthCredentialEmergencyButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:text="@string/work_challenge_emergency_button_text"/>
+ </FrameLayout>
+
+</merge>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
index e439f77..8ac7583 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
@@ -23,84 +23,6 @@
android:elevation="@dimen/biometric_dialog_elevation"
android:theme="?app:attr/lockPinPasswordStyle">
- <RelativeLayout
- android:id="@+id/auth_credential_header"
- style="?headerStyle"
- android:layout_width="wrap_content"
- android:layout_height="match_parent">
-
- <ImageView
- android:id="@+id/icon"
- style="?headerIconStyle"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:contentDescription="@null"/>
-
- <TextView
- android:id="@+id/title"
- style="?titleTextAppearance"
- android:layout_below="@id/icon"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:id="@+id/subtitle"
- style="?subTitleTextAppearance"
- android:layout_below="@id/title"
- android:layout_alignParentLeft="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:id="@+id/description"
- style="?descriptionTextAppearance"
- android:layout_below="@id/subtitle"
- android:layout_alignParentLeft="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </RelativeLayout>
-
- <FrameLayout
- android:id="@+id/auth_credential_input"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top"
- android:orientation="vertical">
-
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="?passwordTextAppearance"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp"/>
-
- <TextView
- android:id="@+id/error"
- style="?errorTextAppearance"
- android:layout_gravity="center_horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-
- <Button
- android:id="@+id/emergencyCallButton"
- style="@style/AuthCredentialEmergencyButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:layout_gravity="center_horizontal|bottom"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:text="@string/work_challenge_emergency_button_text"/>
- </FrameLayout>
+ <include layout="@layout/auth_credential_password_pin_content_view" />
</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pin_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pin_view.xml
new file mode 100644
index 0000000..8ac7583
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/auth_credential_pin_view.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ 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.
+ -->
+
+<com.android.systemui.biometrics.ui.CredentialPasswordView
+ 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="match_parent"
+ android:orientation="horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation"
+ android:theme="?app:attr/lockPinPasswordStyle">
+
+ <include layout="@layout/auth_credential_password_pin_content_view" />
+
+</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml b/packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml
new file mode 100644
index 0000000..11284fd
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml
@@ -0,0 +1,104 @@
+<!--
+ ~ 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.
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <RelativeLayout
+ android:id="@+id/auth_credential_header"
+ style="?headerStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="0dp">
+
+ <ImageView
+ android:id="@+id/icon"
+ style="?headerIconStyle"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@+id/title"
+ style="?titleTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/icon" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="?subTitleTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title" />
+
+ <TextView
+ android:id="@+id/description"
+ style="?descriptionTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/subtitle" />
+
+ </RelativeLayout>
+
+ </ScrollView>
+
+ <FrameLayout
+ android:id="@+id/auth_credential_input"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical">
+
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="?passwordTextAppearance"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp"/>
+
+ <TextView
+ android:id="@+id/error"
+ style="?errorTextAppearance"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/emergencyCallButton"
+ style="@style/AuthCredentialEmergencyButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:text="@string/work_challenge_emergency_button_text"/>
+ </FrameLayout>
+
+</merge>
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 9336845..f8d9a87 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -23,88 +23,6 @@
android:orientation="vertical"
android:theme="?app:attr/lockPinPasswordStyle">
- <ScrollView
- android:id="@+id/auth_credential_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <RelativeLayout
- style="?headerStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@+id/icon"
- style="?headerIconStyle"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:contentDescription="@null" />
-
- <TextView
- android:id="@+id/title"
- style="?titleTextAppearance"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/icon" />
-
- <TextView
- android:id="@+id/subtitle"
- style="?subTitleTextAppearance"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/title" />
-
- <TextView
- android:id="@+id/description"
- style="?descriptionTextAppearance"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/subtitle" />
-
- </RelativeLayout>
-
- </ScrollView>
-
- <FrameLayout
- android:id="@+id/auth_credential_input"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top"
- android:orientation="vertical">
-
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="?passwordTextAppearance"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp"/>
-
- <TextView
- android:id="@+id/error"
- style="?errorTextAppearance"
- android:layout_gravity="center_horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-
- <Button
- android:id="@+id/emergencyCallButton"
- style="@style/AuthCredentialEmergencyButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:layout_gravity="center_horizontal|bottom"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:text="@string/work_challenge_emergency_button_text"/>
- </FrameLayout>
+ <include layout="@layout/auth_credential_password_pin_content_view" />
</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pin_view.xml b/packages/SystemUI/res/layout/auth_credential_pin_view.xml
new file mode 100644
index 0000000..a1cf807
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_pin_view.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ 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.
+ -->
+
+<com.android.systemui.biometrics.ui.CredentialPasswordView
+ 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="match_parent"
+ android:elevation="@dimen/biometric_dialog_elevation"
+ android:orientation="vertical"
+ android:theme="?app:attr/lockPinPasswordStyle">
+
+ <include layout="@layout/auth_credential_password_pin_content_view" />
+
+</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml b/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
index 66c2155..1e5b249 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
@@ -13,15 +13,32 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@android:id/text1"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?androidprv:attr/textColorOnAccent"
- android:singleLine="true"
android:layout_width="match_parent"
- android:layout_height="@dimen/screenrecord_spinner_height"
- android:gravity="center_vertical"
- android:ellipsize="marquee"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/screenrecord_spinner_height"
+ android:paddingEnd="@dimen/screenrecord_spinner_text_padding_end"
android:paddingStart="@dimen/screenrecord_spinner_text_padding_start"
- android:paddingEnd="@dimen/screenrecord_spinner_text_padding_end"/>
\ No newline at end of file
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?androidprv:attr/textColorOnAccent" />
+
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?androidprv:attr/colorError" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index fd1de25..04eae64 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -223,6 +223,9 @@
<!-- Communal mode -->
<item type="id" name="communal_widget_wrapper" />
+ <!-- Values assigned to the views in Biometrics Prompt -->
+ <item type="id" name="pin_pad"/>
+
<!--
Used to tag views programmatically added to the smartspace area so they can be more easily
removed later.
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 77ffe19..b37aeee 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1107,6 +1107,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app">When you’re sharing, recording, or casting an app, <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
<!-- 1P/3P apps media projection permission button to continue with app selection or recording [CHAR LIMIT=60] -->
<string name="media_projection_entry_app_permission_dialog_continue">Start</string>
+ <!-- 1P/3P apps disabled the single app projection option. [CHAR LIMIT=NONE] -->
+ <string name="media_projection_entry_app_permission_dialog_single_app_disabled"><xliff:g id="app_name" example="Meet">%1$s</xliff:g> has disabled this option</string>
<!-- Casting that launched by SysUI (i.e. when there is no app name) -->
<!-- System casting media projection permission dialog title. [CHAR LIMIT=100] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 4bc9491..33e453c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -76,26 +76,11 @@
void onSystemBarAttributesChanged(int displayId, int behavior) = 20;
/**
- * Sent when screen turned on and ready to use (blocker scrim is hidden)
- */
- void onScreenTurnedOn() = 21;
-
- /**
* Sent when the desired dark intensity of the nav buttons has changed
*/
void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
/**
- * Sent when screen started turning on.
- */
- void onScreenTurningOn() = 23;
-
- /**
- * Sent when screen started turning off.
- */
- void onScreenTurningOff() = 24;
-
- /**
* Sent when split keyboard shortcut is triggered to enter stage split.
*/
void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index cab54d0..8200e5c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -39,6 +39,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
import android.view.HapticFeedbackConstants;
@@ -76,6 +77,8 @@
private static final String TAG = "RotationButtonController";
private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
private static final int NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS = 20000;
+ private static final boolean OEM_DISALLOW_ROTATION_IN_SUW =
+ SystemProperties.getBoolean("ro.setupwizard.rotation_locked", false);
private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
@@ -375,6 +378,12 @@
}
public void onRotationProposal(int rotation, boolean isValid) {
+ boolean isUserSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+ if (!isUserSetupComplete && OEM_DISALLOW_ROTATION_IN_SUW) {
+ return;
+ }
+
int windowRotation = mWindowRotationProvider.get();
if (!mRotationButton.acceptRotationProposal()) {
@@ -497,8 +506,7 @@
boolean canShowRotationButton() {
return mIsNavigationBarShowing
|| mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT
- || isGesturalMode(mNavBarMode)
- || mTaskBarVisible;
+ || isGesturalMode(mNavBarMode);
}
@DrawableRes
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index 899cad89..006974c 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -66,7 +66,7 @@
window.isNavigationBarContrastEnforced = false
window.navigationBarColor = Color.TRANSPARENT
- clock = findViewById(R.id.clock)
+ clock = requireViewById(R.id.clock)
keyguardStatusViewController =
keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply {
setDisplayedOnSecondaryDisplay()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 802a550..7464c88 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -499,6 +499,8 @@
R.layout.auth_credential_pattern_view, null, false);
break;
case Utils.CREDENTIAL_PIN:
+ mCredentialView = factory.inflate(R.layout.auth_credential_pin_view, null, false);
+ break;
case Utils.CREDENTIAL_PASSWORD:
mCredentialView = factory.inflate(
R.layout.auth_credential_password_view, null, false);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
index 20c3e40..f7f9103 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
@@ -56,10 +56,10 @@
)
)
} else {
- BoundingBoxOverlapDetector()
+ BoundingBoxOverlapDetector(values[2])
}
} else {
- return BoundingBoxOverlapDetector()
+ return BoundingBoxOverlapDetector(1f)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
index d2cb849..5b0bd95 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
@@ -20,7 +20,10 @@
import android.hardware.face.FaceManager
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
+import android.util.Log
+import com.android.systemui.biometrics.shared.model.LockoutMode
import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.biometrics.shared.model.toLockoutMode
import com.android.systemui.biometrics.shared.model.toSensorStrength
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
@@ -29,16 +32,18 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
/** A repository for the global state of Face sensor. */
interface FacePropertyRepository {
/** Face sensor information, null if it is not available. */
- val sensorInfo: Flow<FaceSensorInfo?>
+ val sensorInfo: StateFlow<FaceSensorInfo?>
+
+ /** Get the current lockout mode for the user. This makes a binder based service call. */
+ suspend fun getLockoutMode(userId: Int): LockoutMode
}
/** Describes a biometric sensor */
@@ -49,33 +54,39 @@
@SysUISingleton
class FacePropertyRepositoryImpl
@Inject
-constructor(@Application private val applicationScope: CoroutineScope, faceManager: FaceManager?) :
- FacePropertyRepository {
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val faceManager: FaceManager?
+) : FacePropertyRepository {
- private val sensorProps: Flow<List<FaceSensorPropertiesInternal>> =
- faceManager?.let {
- ConflatedCallbackFlow.conflatedCallbackFlow {
- val callback =
- object : IFaceAuthenticatorsRegisteredCallback.Stub() {
- override fun onAllAuthenticatorsRegistered(
- sensors: List<FaceSensorPropertiesInternal>
- ) {
- trySendWithFailureLogging(
- sensors,
- TAG,
- "onAllAuthenticatorsRegistered"
- )
- }
+ override val sensorInfo: StateFlow<FaceSensorInfo?> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback =
+ object : IFaceAuthenticatorsRegisteredCallback.Stub() {
+ override fun onAllAuthenticatorsRegistered(
+ sensors: List<FaceSensorPropertiesInternal>,
+ ) {
+ if (sensors.isEmpty()) return
+ trySendWithFailureLogging(
+ FaceSensorInfo(
+ sensors.first().sensorId,
+ sensors.first().sensorStrength.toSensorStrength()
+ ),
+ TAG,
+ "onAllAuthenticatorsRegistered"
+ )
}
- it.addAuthenticatorsRegisteredCallback(callback)
- awaitClose {}
- }
- .shareIn(applicationScope, SharingStarted.Eagerly)
- }
- ?: flowOf(emptyList())
+ }
+ faceManager?.addAuthenticatorsRegisteredCallback(callback)
+ awaitClose {}
+ }
+ .onEach { Log.d(TAG, "sensorProps changed: $it") }
+ .stateIn(applicationScope, SharingStarted.Eagerly, null)
- override val sensorInfo: Flow<FaceSensorInfo?> =
- sensorProps
- .map { it.firstOrNull() }
- .map { it?.let { FaceSensorInfo(it.sensorId, it.sensorStrength.toSensorStrength()) } }
+ override suspend fun getLockoutMode(userId: Int): LockoutMode {
+ if (sensorInfo.value == null || faceManager == null) {
+ return LockoutMode.NONE
+ }
+ return faceManager.getLockoutModeForUser(sensorInfo.value!!.id, userId).toLockoutMode()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LockoutMode.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LockoutMode.kt
new file mode 100644
index 0000000..68bba32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LockoutMode.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.shared.model
+
+import android.hardware.biometrics.BiometricConstants
+
+/** Lockout mode. Represents [BiometricConstants.LockoutMode]. */
+enum class LockoutMode {
+ NONE,
+ TIMED,
+ PERMANENT,
+}
+
+/** Convert [this] to corresponding [LockoutMode] */
+fun Int.toLockoutMode(): LockoutMode =
+ when (this) {
+ BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT -> LockoutMode.PERMANENT
+ BiometricConstants.BIOMETRIC_LOCKOUT_TIMED -> LockoutMode.TIMED
+ else -> LockoutMode.NONE
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
index cf6044f..9b946db 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
@@ -17,16 +17,30 @@
package com.android.systemui.biometrics.udfps
import android.graphics.Rect
+import android.os.Build
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
/** Returns whether the touch coordinates are within the sensor's bounding box. */
@SysUISingleton
-class BoundingBoxOverlapDetector : OverlapDetector {
+class BoundingBoxOverlapDetector(private val targetSize: Float) : OverlapDetector {
+
+ private val TAG = "BoundingBoxOverlapDetector"
+
override fun isGoodOverlap(
touchData: NormalizedTouchData,
nativeSensorBounds: Rect,
nativeOverlayBounds: Rect,
- ): Boolean =
- touchData.isWithinBounds(nativeOverlayBounds) &&
- touchData.isWithinBounds(nativeSensorBounds)
+ ): Boolean {
+ val scaledRadius = (nativeSensorBounds.width() / 2) * targetSize
+ val scaledSensorBounds =
+ Rect(
+ (nativeSensorBounds.centerX() - scaledRadius).toInt(),
+ (nativeSensorBounds.centerY() - scaledRadius).toInt(),
+ (nativeSensorBounds.centerX() + scaledRadius).toInt(),
+ (nativeSensorBounds.centerY() + scaledRadius).toInt(),
+ )
+
+ return touchData.isWithinBounds(scaledSensorBounds)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index 709fe85..6c5cc48 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -8,11 +8,8 @@
import android.view.WindowInsets
import android.view.WindowInsets.Type.ime
import android.view.accessibility.AccessibilityManager
-import android.widget.ImageView
-import android.widget.ImeAwareEditText
import android.widget.LinearLayout
import android.widget.TextView
-import androidx.core.view.isGone
import com.android.systemui.R
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.ui.binder.CredentialViewBinder
@@ -22,14 +19,6 @@
class CredentialPasswordView(context: Context, attrs: AttributeSet?) :
LinearLayout(context, attrs), CredentialView, View.OnApplyWindowInsetsListener {
- private lateinit var titleView: TextView
- private lateinit var subtitleView: TextView
- private lateinit var descriptionView: TextView
- private lateinit var iconView: ImageView
- private lateinit var passwordField: ImeAwareEditText
- private lateinit var credentialHeader: View
- private lateinit var credentialInput: View
-
private var bottomInset: Int = 0
private val accessibilityManager by lazy {
@@ -48,90 +37,32 @@
override fun onFinishInflate() {
super.onFinishInflate()
-
- titleView = requireViewById(R.id.title)
- subtitleView = requireViewById(R.id.subtitle)
- descriptionView = requireViewById(R.id.description)
- iconView = requireViewById(R.id.icon)
- passwordField = requireViewById(R.id.lockPassword)
- credentialHeader = requireViewById(R.id.auth_credential_header)
- credentialInput = requireViewById(R.id.auth_credential_input)
-
setOnApplyWindowInsetsListener(this)
}
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
- super.onLayout(changed, left, top, right, bottom)
-
- val inputLeftBound: Int
- var inputTopBound: Int
- var headerRightBound = right
- var headerTopBounds = top
- var headerBottomBounds = bottom
- val subTitleBottom: Int = if (subtitleView.isGone) titleView.bottom else subtitleView.bottom
- val descBottom = if (descriptionView.isGone) subTitleBottom else descriptionView.bottom
- if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
- inputTopBound = (bottom - credentialInput.height) / 2
- inputLeftBound = (right - left) / 2
- headerRightBound = inputLeftBound
- if (descriptionView.bottom > headerBottomBounds) {
- headerTopBounds -= iconView.bottom.coerceAtMost(bottomInset)
- credentialHeader.layout(left, headerTopBounds, headerRightBound, bottom)
- }
- } else {
- inputTopBound = descBottom + (bottom - descBottom - credentialInput.height) / 2
- inputLeftBound = (right - left - credentialInput.width) / 2
-
- if (bottom - inputTopBound < credentialInput.height) {
- inputTopBound = bottom - credentialInput.height
- }
-
- if (descriptionView.bottom > inputTopBound) {
- credentialHeader.layout(left, headerTopBounds, headerRightBound, inputTopBound)
- }
- }
-
- credentialInput.layout(inputLeftBound, inputTopBound, right, bottom)
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
-
- val newWidth = MeasureSpec.getSize(widthMeasureSpec)
- val newHeight = MeasureSpec.getSize(heightMeasureSpec) - bottomInset
-
- setMeasuredDimension(newWidth, newHeight)
-
- val halfWidthSpec = MeasureSpec.makeMeasureSpec(width / 2, MeasureSpec.AT_MOST)
- val fullHeightSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.UNSPECIFIED)
- if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
- measureChildren(halfWidthSpec, fullHeightSpec)
- } else {
- measureChildren(widthMeasureSpec, fullHeightSpec)
- }
- }
-
override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
- val bottomInsets = insets.getInsets(ime())
- if (bottomInset != bottomInsets.bottom) {
- bottomInset = bottomInsets.bottom
-
- if (bottomInset > 0 && resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
- titleView.isSingleLine = true
- titleView.ellipsize = TextUtils.TruncateAt.MARQUEE
- titleView.marqueeRepeatLimit = -1
- // select to enable marquee unless a screen reader is enabled
- titleView.isSelected = accessibilityManager?.shouldMarquee() ?: false
- } else {
- titleView.isSingleLine = false
- titleView.ellipsize = null
- // select to enable marquee unless a screen reader is enabled
- titleView.isSelected = false
+ val imeBottomInset = insets.getInsets(ime()).bottom
+ if (bottomInset != imeBottomInset) {
+ val titleView: TextView? = findViewById(R.id.title)
+ if (titleView != null) {
+ if (
+ bottomInset > 0 && resources.configuration.orientation == ORIENTATION_LANDSCAPE
+ ) {
+ titleView.isSingleLine = true
+ titleView.ellipsize = TextUtils.TruncateAt.MARQUEE
+ titleView.marqueeRepeatLimit = -1
+ // select to enable marquee unless a screen reader is enabled
+ titleView.isSelected = accessibilityManager.shouldMarquee()
+ } else {
+ titleView.isSingleLine = false
+ titleView.ellipsize = null
+ // select to enable marquee unless a screen reader is enabled
+ titleView.isSelected = false
+ }
}
-
- requestLayout()
}
- return insets
+ setPadding(paddingLeft, paddingTop, paddingRight, imeBottomInset)
+ return insets.inset(0, 0, 0, imeBottomInset)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt
new file mode 100644
index 0000000..cf6865c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ui
+
+/**
+ * Interface for PinPad in auth_credential_pin_view. This is needed when a custom pin pad is
+ * preferred to the IME To use a PinPad, one needs to implement IPinPad interface and provide it in
+ * auth_credential_pin_view and specify the id as [pin_pad]
+ */
+interface IPinPad {
+ fun setPinPadClickListener(pinPadClickListener: PinPadClickListener)
+}
+
+/** The call back interface for onClick event in the view. */
+interface PinPadClickListener {
+ /**
+ * One of the digit key has been clicked.
+ *
+ * @param digit A String representing a digit between 0 and 9.
+ */
+ fun onDigitKeyClick(digit: String?)
+
+ /** The backspace key has been clicked. */
+ fun onBackspaceClick()
+
+ /** The enter key has been clicked. */
+ fun onEnterKeyClick()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
index c27d7152..996b62e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
@@ -15,6 +15,7 @@
import com.android.systemui.R
import com.android.systemui.biometrics.ui.CredentialPasswordView
import com.android.systemui.biometrics.ui.CredentialView
+import com.android.systemui.biometrics.ui.IPinPad
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import kotlinx.coroutines.awaitCancellation
@@ -53,13 +54,19 @@
}
)
passwordField.setOnKeyListener(OnBackButtonListener(onBackInvokedCallback))
-
+ val pinPadView = view.findViewById(R.id.pin_pad) as? IPinPad
+ if (pinPadView != null) {
+ PinPadViewBinder.bind(pinPadView, view)
+ }
repeatOnLifecycle(Lifecycle.State.STARTED) {
// dismiss on a valid credential check
launch {
viewModel.validatedAttestation.collect { attestation ->
if (attestation != null) {
- imeManager.hideSoftInputFromWindow(view.windowToken, 0 /* flags */)
+ imeManager.hideSoftInputFromWindow(
+ view.windowToken,
+ 0 // flag
+ )
host.onCredentialMatched(attestation)
} else {
passwordField.setText("")
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 4ac9f96..25fe619 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -47,6 +47,7 @@
val descriptionView: TextView = view.requireViewById(R.id.description)
val iconView: ImageView? = view.findViewById(R.id.icon)
val errorView: TextView = view.requireViewById(R.id.error)
+ val cancelButton: Button? = view.findViewById(R.id.cancel_button)
val emergencyButtonView: Button = view.requireViewById(R.id.emergencyCallButton)
var errorTimer: Job? = null
@@ -60,7 +61,7 @@
updateForContentDimensions(
containerWidth,
containerHeight,
- 0 /* animateDurationMs */
+ 0 // animateDurationMs
)
}
}
@@ -103,7 +104,18 @@
}
}
}
- .collect { errorView.textOrHide = it }
+ .collect { it ->
+ val hasError = !it.isNullOrBlank()
+ errorView.visibility =
+ if (hasError) {
+ View.VISIBLE
+ } else if (cancelButton != null) {
+ View.INVISIBLE
+ } else {
+ View.GONE
+ }
+ errorView.text = if (hasError) it else ""
+ }
}
// show an extra dialog if the remaining attempts becomes low
@@ -117,6 +129,8 @@
}
}
+ cancelButton?.setOnClickListener { host.onCredentialAborted() }
+
// bind the auth widget
when (view) {
is CredentialPasswordView ->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt
new file mode 100644
index 0000000..906206c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ui.binder
+
+import android.view.KeyEvent
+import android.widget.ImeAwareEditText
+import com.android.internal.widget.LockscreenCredential
+import com.android.systemui.R
+import com.android.systemui.biometrics.ui.CredentialPasswordView
+import com.android.systemui.biometrics.ui.IPinPad
+import com.android.systemui.biometrics.ui.PinPadClickListener
+
+/** Binder for IPinPad */
+object PinPadViewBinder {
+ /** Implements a PinPadClickListener inside a pin pad */
+ @JvmStatic
+ fun bind(view: IPinPad, credentialPasswordView: CredentialPasswordView) {
+ val passwordField: ImeAwareEditText =
+ credentialPasswordView.requireViewById(R.id.lockPassword)
+ view.setPinPadClickListener(
+ object : PinPadClickListener {
+
+ override fun onDigitKeyClick(digit: String?) {
+ passwordField.append(digit)
+ }
+
+ override fun onBackspaceClick() {
+ val pin = LockscreenCredential.createPinOrNone(passwordField.text)
+ if (pin.size() > 0) {
+ passwordField.text.delete(
+ passwordField.selectionEnd - 1,
+ passwordField.selectionEnd
+ )
+ }
+ pin.zeroize()
+ }
+
+ override fun onEnterKeyClick() {
+ passwordField.dispatchKeyEvent(
+ KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0)
+ )
+ passwordField.dispatchKeyEvent(
+ KeyEvent(0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0)
+ )
+ }
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 4dc7720..e8b8f54 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -289,7 +289,7 @@
if (authenticateAfterError) {
showAuthenticating(messageAfterError)
} else {
- showHelp(messageAfterError)
+ showInfo(messageAfterError)
}
}
}
@@ -309,12 +309,15 @@
private fun supportsRetry(failedModality: BiometricModality) =
failedModality == BiometricModality.Face
+ suspend fun showHelp(message: String) = showHelp(message, clearIconError = false)
+ suspend fun showInfo(message: String) = showHelp(message, clearIconError = true)
+
/**
* Show a persistent help message.
*
* Will be show even if the user has already authenticated.
*/
- suspend fun showHelp(message: String) {
+ private suspend fun showHelp(message: String, clearIconError: Boolean) {
val alreadyAuthenticated = _isAuthenticated.value.isAuthenticated
if (!alreadyAuthenticated) {
_isAuthenticating.value = false
@@ -329,6 +332,8 @@
AuthBiometricView.STATE_PENDING_CONFIRMATION
} else if (alreadyAuthenticated && !isConfirmationRequired.first()) {
AuthBiometricView.STATE_AUTHENTICATED
+ } else if (clearIconError) {
+ AuthBiometricView.STATE_IDLE
} else {
AuthBiometricView.STATE_HELP
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
index 918e168..f2b4e09 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
@@ -58,7 +58,7 @@
* ```
*/
val panelExpansionAmount: StateFlow<Float>
- val keyguardPosition: StateFlow<Float>
+ val keyguardPosition: StateFlow<Float?>
val isBackButtonEnabled: StateFlow<Boolean?>
/** Determines if user is already unlocked */
val keyguardAuthenticated: StateFlow<Boolean?>
@@ -130,7 +130,7 @@
*/
private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
override val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
- private val _keyguardPosition = MutableStateFlow(0f)
+ private val _keyguardPosition = MutableStateFlow<Float?>(null)
override val keyguardPosition = _keyguardPosition.asStateFlow()
private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
@@ -244,6 +244,7 @@
.logDiffsForTable(buffer, "", "PanelExpansionAmountMillis", -1)
.launchIn(applicationScope)
keyguardPosition
+ .filterNotNull()
.map { it.toInt() }
.logDiffsForTable(buffer, "", "KeyguardPosition", -1)
.launchIn(applicationScope)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index c486603..0e0f1f6 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -94,7 +94,7 @@
val startingDisappearAnimation: Flow<Runnable> =
repository.primaryBouncerStartingDisappearAnimation.filterNotNull()
val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it }
- val keyguardPosition: Flow<Float> = repository.keyguardPosition
+ val keyguardPosition: Flow<Float> = repository.keyguardPosition.filterNotNull()
val panelExpansionAmount: Flow<Float> = repository.panelExpansionAmount
/** 0f = bouncer fully hidden. 1f = bouncer fully visible. */
val bouncerExpansion: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 484be9c..1b2a9eb 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -49,6 +49,7 @@
import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.ImmersiveModeConfirmation
+import com.android.systemui.statusbar.gesture.GesturePointerEventListener
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
import com.android.systemui.statusbar.phone.LockscreenWallpaper
@@ -182,6 +183,12 @@
@ClassKey(ScreenDecorations::class)
abstract fun bindScreenDecorations(sysui: ScreenDecorations): CoreStartable
+ /** Inject into GesturePointerEventHandler. */
+ @Binds
+ @IntoMap
+ @ClassKey(GesturePointerEventListener::class)
+ abstract fun bindGesturePointerEventListener(sysui: GesturePointerEventListener): CoreStartable
+
/** Inject into SessionTracker. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 93eb103..8064cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -52,6 +52,7 @@
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
import java.io.PrintWriter
import java.util.Arrays
@@ -71,6 +72,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@@ -200,7 +202,7 @@
private val keyguardSessionId: InstanceId?
get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
- private val _canRunFaceAuth = MutableStateFlow(true)
+ private val _canRunFaceAuth = MutableStateFlow(false)
override val canRunFaceAuth: StateFlow<Boolean>
get() = _canRunFaceAuth
@@ -281,7 +283,9 @@
} else {
keyguardRepository.isKeyguardGoingAway
},
- userRepository.userSwitchingInProgress,
+ userRepository.selectedUser.map {
+ it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
+ },
)
.onEach { anyOfThemIsTrue ->
if (anyOfThemIsTrue) {
@@ -325,6 +329,7 @@
cancelDetection()
}
}
+ .flowOn(mainDispatcher)
.logDiffsForTable(faceDetectLog, "", "canFaceDetectRun", false)
.launchIn(applicationScope)
}
@@ -410,6 +415,7 @@
cancel()
}
}
+ .flowOn(mainDispatcher)
.logDiffsForTable(faceAuthLog, "", "canFaceAuthRun", false)
.launchIn(applicationScope)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index e501ece..635961b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -54,11 +54,7 @@
if (event.handleAction()) {
when (event.keyCode) {
KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
- KeyEvent.KEYCODE_SPACE,
- KeyEvent.KEYCODE_ENTER ->
- if (isDeviceInteractive()) {
- return collapseShadeLockedOrShowPrimaryBouncer()
- }
+ KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
}
}
return false
@@ -94,22 +90,16 @@
(statusBarStateController.state != StatusBarState.SHADE) &&
statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
if (shouldUnlockOnMenuPressed) {
- return collapseShadeLockedOrShowPrimaryBouncer()
+ shadeController.animateCollapseShadeForced()
+ return true
}
return false
}
- private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean {
- when (statusBarStateController.state) {
- StatusBarState.SHADE -> return false
- StatusBarState.SHADE_LOCKED -> {
- shadeController.animateCollapseShadeForced()
- return true
- }
- StatusBarState.KEYGUARD -> {
- statusBarKeyguardViewManager.showPrimaryBouncer(true)
- return true
- }
+ private fun dispatchSpaceEvent(): Boolean {
+ if (isDeviceInteractive() && statusBarStateController.state != StatusBarState.SHADE) {
+ shadeController.animateCollapseShadeForced()
+ return true
}
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index 2a3f852..4b8171f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -22,6 +22,8 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.CoreStartable
import com.android.systemui.R
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
+import com.android.systemui.biometrics.shared.model.LockoutMode
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -35,6 +37,7 @@
import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
@@ -51,6 +54,7 @@
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import kotlinx.coroutines.yield
/**
* Encapsulates business logic related face authentication being triggered for device entry from
@@ -72,6 +76,7 @@
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val userRepository: UserRepository,
+ private val facePropertyRepository: FacePropertyRepository,
) : CoreStartable, KeyguardFaceAuthInteractor {
private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
@@ -92,7 +97,7 @@
faceAuthenticationLogger.bouncerVisibilityChanged()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN,
- fallbackToDetect = true
+ fallbackToDetect = false
)
}
.launchIn(applicationScope)
@@ -134,16 +139,25 @@
// User switching should stop face auth and then when it is complete we should trigger face
// auth so that the switched user can unlock the device with face auth.
- userRepository.userSwitchingInProgress
- .pairwise(false)
- .onEach { (wasSwitching, isSwitching) ->
+ userRepository.selectedUser
+ .pairwise()
+ .onEach { (previous, curr) ->
+ val wasSwitching = previous.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
+ val isSwitching = curr.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
if (!wasSwitching && isSwitching) {
repository.pauseFaceAuth()
} else if (wasSwitching && !isSwitching) {
+ val lockoutMode = facePropertyRepository.getLockoutMode(curr.userInfo.id)
+ if (lockoutMode == LockoutMode.PERMANENT || lockoutMode == LockoutMode.TIMED) {
+ repository.lockoutFaceAuth()
+ }
repository.resumeFaceAuth()
+ yield()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
- fallbackToDetect = true
+ // Fallback to detection if bouncer is not showing so that we can detect a
+ // face and then show the bouncer to the user if face auth can't run
+ fallbackToDetect = !primaryBouncerInteractor.isBouncerShowing()
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt
new file mode 100644
index 0000000..c2d2725
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard.qualifiers
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KeyguardRootView
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
index a5b00e0..c8dab32 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
@@ -87,7 +87,7 @@
}
addListener(
object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
+ override fun onAnimationEnd(animation: Animator) {
updateIsAnimatingSurface()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt
new file mode 100644
index 0000000..c88737e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.view
+
+import android.view.View
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.KeyguardViewConfigurator
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import dagger.Module
+import dagger.Provides
+import javax.inject.Provider
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@Module
+object LockscreenSceneModule {
+
+ @Provides
+ @SysUISingleton
+ @KeyguardRootView
+ fun viewProvider(
+ configurator: Provider<KeyguardViewConfigurator>,
+ ): () -> View {
+ return { configurator.get().getKeyguardRootView() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 6d3b7f1..93c4902 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,41 +16,22 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.R
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
/** Models UI state and handles user input for the lockscreen scene. */
@SysUISingleton
class LockscreenSceneViewModel
@Inject
constructor(
- @Application applicationScope: CoroutineScope,
authenticationInteractor: AuthenticationInteractor,
private val bouncerInteractor: BouncerInteractor,
) {
- /** The icon for the "lock" button on the lockscreen. */
- val lockButtonIcon: StateFlow<Icon> =
- authenticationInteractor.isUnlocked
- .map { isUnlocked -> lockIcon(isUnlocked = isUnlocked) }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = lockIcon(isUnlocked = authenticationInteractor.isUnlocked.value),
- )
-
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: Flow<SceneKey> =
authenticationInteractor.isUnlocked.map { isUnlocked ->
@@ -65,31 +46,4 @@
fun onLockButtonClicked() {
bouncerInteractor.showOrUnlockDevice()
}
-
- /** Notifies that some content on the lock screen was clicked. */
- fun onContentClicked() {
- bouncerInteractor.showOrUnlockDevice()
- }
-
- private fun upDestinationSceneKey(
- canSwipeToDismiss: Boolean,
- ): SceneKey {
- return if (canSwipeToDismiss) SceneKey.Gone else SceneKey.Bouncer
- }
-
- private fun lockIcon(
- isUnlocked: Boolean,
- ): Icon {
- return if (isUnlocked) {
- Icon.Resource(
- R.drawable.ic_device_lock_off,
- ContentDescription.Resource(R.string.accessibility_unlock_button)
- )
- } else {
- Icon.Resource(
- R.drawable.ic_device_lock_on,
- ContentDescription.Resource(R.string.accessibility_lock_icon)
- )
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 72352e3..a9d2b30 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -36,6 +36,7 @@
import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.media.projection.IMediaProjection;
+import android.media.projection.MediaProjectionConfig;
import android.media.projection.MediaProjectionManager;
import android.media.projection.ReviewGrantedConsentResult;
import android.os.Bundle;
@@ -54,6 +55,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
import com.android.systemui.screenrecord.ScreenShareOption;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -71,6 +73,7 @@
private final FeatureFlags mFeatureFlags;
private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
+ private final ActivityStarter mActivityStarter;
private String mPackageName;
private int mUid;
@@ -86,8 +89,10 @@
@Inject
public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
- Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver) {
+ Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
+ ActivityStarter activityStarter) {
mFeatureFlags = featureFlags;
+ mActivityStarter = activityStarter;
mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
}
@@ -208,11 +213,13 @@
// the correct screen width when in split screen.
Context dialogContext = getApplicationContext();
if (isPartialScreenSharingEnabled()) {
- mDialog = new MediaProjectionPermissionDialog(dialogContext, () -> {
- ScreenShareOption selectedOption =
- ((MediaProjectionPermissionDialog) mDialog).getSelectedScreenShareOption();
- grantMediaProjectionPermission(selectedOption.getMode());
- }, () -> finish(RECORD_CANCEL, /* projection= */ null), appName);
+ mDialog = new MediaProjectionPermissionDialog(dialogContext, getMediaProjectionConfig(),
+ () -> {
+ MediaProjectionPermissionDialog dialog =
+ (MediaProjectionPermissionDialog) mDialog;
+ ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
+ grantMediaProjectionPermission(selectedOption.getMode());
+ }, () -> finish(RECORD_CANCEL, /* projection= */ null), appName);
} else {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(dialogContext,
R.style.Theme_SystemUI_Dialog)
@@ -306,8 +313,16 @@
// Start activity from the current foreground user to avoid creating a separate
// SystemUI process without access to recent tasks because it won't have
// WM Shell running inside.
+ // It is also important to make sure the shade is dismissed, otherwise users won't
+ // see the app selector.
mUserSelectingTask = true;
- startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
+ mActivityStarter.startActivity(
+ intent,
+ /* dismissShade= */ true,
+ /* animationController= */ null,
+ /* showOverLockscreenWhenLocked= */ false,
+ UserHandle.of(ActivityManager.getCurrentUser())
+ );
}
} catch (RemoteException e) {
Log.e(TAG, "Error granting projection permission", e);
@@ -348,6 +363,16 @@
}
}
+ @Nullable
+ private MediaProjectionConfig getMediaProjectionConfig() {
+ Intent intent = getIntent();
+ if (intent == null) {
+ return null;
+ }
+ return intent.getParcelableExtra(
+ MediaProjectionManager.EXTRA_MEDIA_PROJECTION_CONFIG);
+ }
+
private boolean isPartialScreenSharingEnabled() {
return mFeatureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9f45f66..1e82d44 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -87,7 +87,6 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBar;
@@ -571,7 +570,6 @@
SysUiState sysUiState,
Provider<SceneInteractor> sceneInteractor,
UserTracker userTracker,
- ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle,
UiEventLogger uiEventLogger,
DisplayTracker displayTracker,
@@ -651,7 +649,6 @@
// Listen for user setup
mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
- screenLifecycle.addObserver(mScreenLifecycleObserver);
wakefulnessLifecycle.addObserver(mWakefulnessLifecycleObserver);
// Connect to the service
updateEnabledAndBinding();
@@ -923,60 +920,6 @@
}
}
- private final ScreenLifecycle.Observer mScreenLifecycleObserver =
- new ScreenLifecycle.Observer() {
- /**
- * Notifies the Launcher that screen turned on and ready to use
- */
- @Override
- public void onScreenTurnedOn() {
- try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onScreenTurnedOn();
- } else {
- Log.e(TAG_OPS,
- "Failed to get overview proxy for screen turned on event.");
- }
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onScreenTurnedOn()", e);
- }
- }
-
- /**
- * Notifies the Launcher that screen is starting to turn on.
- */
- @Override
- public void onScreenTurningOff() {
- try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onScreenTurningOff();
- } else {
- Log.e(TAG_OPS,
- "Failed to get overview proxy for screen turning off event.");
- }
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onScreenTurningOff()", e);
- }
- }
-
- /**
- * Notifies the Launcher that screen is starting to turn on.
- */
- @Override
- public void onScreenTurningOn() {
- try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onScreenTurningOn();
- } else {
- Log.e(TAG_OPS,
- "Failed to get overview proxy for screen turning on event.");
- }
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onScreenTurningOn()", e);
- }
- }
- };
-
private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
new WakefulnessLifecycle.Observer() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 398e64b..7147951 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.scene
+import com.android.systemui.keyguard.ui.view.LockscreenSceneModule
import com.android.systemui.scene.domain.startable.SceneContainerStartableModule
import com.android.systemui.scene.shared.model.SceneContainerConfigModule
import com.android.systemui.scene.ui.composable.SceneModule
@@ -24,6 +25,7 @@
@Module(
includes =
[
+ LockscreenSceneModule::class,
SceneContainerConfigModule::class,
SceneContainerStartableModule::class,
SceneModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
index 23894a3..7859fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -18,7 +18,9 @@
import android.content.Context
import android.os.Bundle
import android.view.Gravity
+import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup
import android.view.ViewStub
import android.view.WindowManager
import android.widget.AdapterView
@@ -35,7 +37,7 @@
/** Base permission dialog for screen share and recording */
open class BaseScreenSharePermissionDialog(
- context: Context?,
+ context: Context,
private val screenShareOptions: List<ScreenShareOption>,
private val appName: String?,
@DrawableRes private val dialogIconDrawable: Int? = null,
@@ -82,14 +84,7 @@
get() = context.getString(selectedScreenShareOption.warningText, appName)
private fun initScreenShareSpinner() {
- val options = screenShareOptions.map { context.getString(it.spinnerText) }.toTypedArray()
- val adapter =
- ArrayAdapter(
- context.applicationContext,
- R.layout.screen_share_dialog_spinner_text,
- options
- )
- adapter.setDropDownViewResource(R.layout.screen_share_dialog_spinner_item_text)
+ val adapter = OptionsAdapter(context.applicationContext, screenShareOptions)
screenShareModeSpinner = requireViewById(R.id.screen_share_mode_spinner)
screenShareModeSpinner.adapter = adapter
screenShareModeSpinner.onItemSelectedListener = this
@@ -131,3 +126,35 @@
stub.inflate()
}
}
+
+private class OptionsAdapter(
+ context: Context,
+ private val options: List<ScreenShareOption>,
+) :
+ ArrayAdapter<String>(
+ context,
+ R.layout.screen_share_dialog_spinner_text,
+ options.map { context.getString(it.spinnerText) }
+ ) {
+
+ override fun isEnabled(position: Int): Boolean {
+ return options[position].spinnerDisabledText == null
+ }
+
+ override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val inflater = LayoutInflater.from(parent.context)
+ val view = inflater.inflate(R.layout.screen_share_dialog_spinner_item_text, parent, false)
+ val titleTextView = view.findViewById<TextView>(android.R.id.text1)
+ val errorTextView = view.findViewById<TextView>(android.R.id.text2)
+ titleTextView.text = getItem(position)
+ errorTextView.text = options[position].spinnerDisabledText
+ if (isEnabled(position)) {
+ errorTextView.visibility = View.GONE
+ titleTextView.isEnabled = true
+ } else {
+ errorTextView.visibility = View.VISIBLE
+ titleTextView.isEnabled = false
+ }
+ return view
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
index f4f5f66..8cbc4aab 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
@@ -16,16 +16,23 @@
package com.android.systemui.screenrecord
import android.content.Context
+import android.media.projection.MediaProjectionConfig
import android.os.Bundle
import com.android.systemui.R
/** Dialog to select screen recording options */
class MediaProjectionPermissionDialog(
- context: Context?,
+ context: Context,
+ mediaProjectionConfig: MediaProjectionConfig?,
private val onStartRecordingClicked: Runnable,
private val onCancelClicked: Runnable,
private val appName: String?
-) : BaseScreenSharePermissionDialog(context, createOptionList(appName), appName) {
+) :
+ BaseScreenSharePermissionDialog(
+ context,
+ createOptionList(context, appName, mediaProjectionConfig),
+ appName
+ ) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// TODO(b/270018943): Handle the case of System sharing (not recording nor casting)
@@ -49,7 +56,11 @@
}
companion object {
- private fun createOptionList(appName: String?): List<ScreenShareOption> {
+ private fun createOptionList(
+ context: Context,
+ appName: String?,
+ mediaProjectionConfig: MediaProjectionConfig?
+ ): List<ScreenShareOption> {
val singleAppWarningText =
if (appName == null) {
R.string.media_projection_entry_cast_permission_dialog_warning_single_app
@@ -63,6 +74,19 @@
R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
}
+ val singleAppDisabledText =
+ if (
+ appName != null &&
+ mediaProjectionConfig?.regionToCapture ==
+ MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
+ ) {
+ context.getString(
+ R.string.media_projection_entry_app_permission_dialog_single_app_disabled,
+ appName
+ )
+ } else {
+ null
+ }
return listOf(
ScreenShareOption(
mode = ENTIRE_SCREEN,
@@ -72,7 +96,8 @@
ScreenShareOption(
mode = SINGLE_APP,
spinnerText = R.string.screen_share_permission_dialog_option_single_app,
- warningText = singleAppWarningText
+ warningText = singleAppWarningText,
+ spinnerDisabledText = singleAppDisabledText,
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index fb99775..9c5da10 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -41,7 +41,7 @@
/** Dialog to select screen recording options */
class ScreenRecordPermissionDialog(
- context: Context?,
+ context: Context,
private val hostUserHandle: UserHandle,
private val controller: RecordingController,
private val activityStarter: ActivityStarter,
@@ -52,7 +52,7 @@
BaseScreenSharePermissionDialog(
context,
createOptionList(),
- null,
+ appName = null,
R.drawable.ic_screenrecord,
R.color.screenrecord_icon_color
) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
index 3d39fd8..ebf0dd2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
@@ -29,5 +29,6 @@
class ScreenShareOption(
@ScreenShareMode val mode: Int,
@StringRes val spinnerText: Int,
- @StringRes val warningText: Int
+ @StringRes val warningText: Int,
+ val spinnerDisabledText: String? = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 6c8190a..d0d37c4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -25,6 +25,7 @@
import androidx.annotation.Nullable;
+import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
@@ -52,6 +53,7 @@
private BrightnessMirrorController mMirrorController;
private boolean mTracking;
private final FalsingManager mFalsingManager;
+ private final UiEventLogger mUiEventLogger;
private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
@Override
@@ -72,9 +74,11 @@
BrightnessSliderController(
BrightnessSliderView brightnessSliderView,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager,
+ UiEventLogger uiEventLogger) {
super(brightnessSliderView);
mFalsingManager = falsingManager;
+ mUiEventLogger = uiEventLogger;
}
/**
@@ -206,7 +210,7 @@
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mTracking = true;
-
+ mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STARTED_TRACKING_TOUCH);
if (mListener != null) {
mListener.onChanged(mTracking, getValue(), false);
}
@@ -220,7 +224,7 @@
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mTracking = false;
-
+ mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STOPPED_TRACKING_TOUCH);
if (mListener != null) {
mListener.onChanged(mTracking, getValue(), true);
}
@@ -237,10 +241,12 @@
public static class Factory {
private final FalsingManager mFalsingManager;
+ private final UiEventLogger mUiEventLogger;
@Inject
- public Factory(FalsingManager falsingManager) {
+ public Factory(FalsingManager falsingManager, UiEventLogger uiEventLogger) {
mFalsingManager = falsingManager;
+ mUiEventLogger = uiEventLogger;
}
/**
@@ -250,11 +256,13 @@
* @param viewRoot the {@link ViewGroup} that will contain the hierarchy. The inflated
* hierarchy will not be attached
*/
- public BrightnessSliderController create(Context context, @Nullable ViewGroup viewRoot) {
+ public BrightnessSliderController create(
+ Context context,
+ @Nullable ViewGroup viewRoot) {
int layout = getLayout();
BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
.inflate(layout, viewRoot, false);
- return new BrightnessSliderController(root, mFalsingManager);
+ return new BrightnessSliderController(root, mFalsingManager, mUiEventLogger);
}
/** Get the layout to inflate based on what slider to use */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java
new file mode 100644
index 0000000..3a30880
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings.brightness;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+public enum BrightnessSliderEvent implements UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "slider started to track touch")
+ SLIDER_STARTED_TRACKING_TOUCH(1472),
+ @UiEvent(doc = "slider stopped tracking touch")
+ SLIDER_STOPPED_TRACKING_TOUCH(1473);
+
+ private final int mId;
+
+ BrightnessSliderEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
new file mode 100644
index 0000000..b34c3ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.gesture
+
+import android.content.Context
+import android.view.InputEvent
+import android.view.MotionEvent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
+import javax.inject.Inject
+
+/**
+ * A class to detect when a motion event happens. To be notified when the event is detected, add a
+ * callback via [addOnGestureDetectedCallback].
+ */
+@SysUISingleton
+class GesturePointerEventDetector @Inject constructor(
+ private val context: Context,
+ displayTracker: DisplayTracker
+) : GenericGestureDetector(
+ GesturePointerEventDetector::class.simpleName!!,
+ displayTracker.defaultDisplayId
+) {
+ override fun onInputEvent(ev: InputEvent) {
+ if (ev !is MotionEvent) {
+ return
+ }
+ // Pass all events to [gestureDetector], which will then notify [gestureListener] when a tap
+ // is detected.
+ onGestureDetected(ev)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt
new file mode 100644
index 0000000..8505c5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.gesture
+
+import android.content.Context
+import android.graphics.Rect
+import android.graphics.Region
+import android.hardware.display.DisplayManagerGlobal
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.util.Log
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.GestureDetector
+import android.view.InputDevice
+import android.view.InputEvent
+import android.view.MotionEvent
+import android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT
+import android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE
+import android.view.ViewRootImpl.CLIENT_TRANSIENT
+import android.widget.OverScroller
+import com.android.internal.R
+import com.android.systemui.CoreStartable
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Watches for gesture events that may trigger system bar related events and notify the registered
+ * callbacks. Add callback to this listener by calling {@link setCallbacks}.
+ */
+class GesturePointerEventListener
+@Inject
+constructor(context: Context, gestureDetector: GesturePointerEventDetector) : CoreStartable {
+ private val mContext: Context
+ private val mHandler = Handler(Looper.getMainLooper())
+ private var mGestureDetector: GesturePointerEventDetector
+ private var mFlingGestureDetector: GestureDetector? = null
+ private var mDisplayCutoutTouchableRegionSize = 0
+
+ // The thresholds for each edge of the display
+ private val mSwipeStartThreshold = Rect()
+ private var mSwipeDistanceThreshold = 0
+ private var mCallbacks: Callbacks? = null
+ private val mDownPointerId = IntArray(MAX_TRACKED_POINTERS)
+ private val mDownX = FloatArray(MAX_TRACKED_POINTERS)
+ private val mDownY = FloatArray(MAX_TRACKED_POINTERS)
+ private val mDownTime = LongArray(MAX_TRACKED_POINTERS)
+ var screenHeight = 0
+ var screenWidth = 0
+ private var mDownPointers = 0
+ private var mSwipeFireable = false
+ private var mDebugFireable = false
+ private var mMouseHoveringAtLeft = false
+ private var mMouseHoveringAtTop = false
+ private var mMouseHoveringAtRight = false
+ private var mMouseHoveringAtBottom = false
+ private var mLastFlingTime: Long = 0
+
+ init {
+ mContext = checkNull("context", context)
+ mGestureDetector = checkNull("gesture detector", gestureDetector)
+ onConfigurationChanged()
+ }
+
+ override fun start() {
+ if (!CLIENT_TRANSIENT) {
+ return
+ }
+ mGestureDetector.addOnGestureDetectedCallback(TAG) { ev -> onInputEvent(ev) }
+ mGestureDetector.startGestureListening()
+
+ mFlingGestureDetector =
+ object : GestureDetector(mContext, FlingGestureDetector(), mHandler) {}
+ }
+
+ fun onDisplayInfoChanged(info: DisplayInfo) {
+ screenWidth = info.logicalWidth
+ screenHeight = info.logicalHeight
+ onConfigurationChanged()
+ }
+
+ fun onConfigurationChanged() {
+ if (!CLIENT_TRANSIENT) {
+ return
+ }
+ val r = mContext.resources
+ val defaultThreshold = r.getDimensionPixelSize(R.dimen.system_gestures_start_threshold)
+ mSwipeStartThreshold[defaultThreshold, defaultThreshold, defaultThreshold] =
+ defaultThreshold
+ mSwipeDistanceThreshold = defaultThreshold
+ val display = DisplayManagerGlobal.getInstance().getRealDisplay(mContext.displayId)
+ val displayCutout = display.cutout
+ if (displayCutout != null) {
+ // Expand swipe start threshold such that we can catch touches that just start beyond
+ // the notch area
+ mDisplayCutoutTouchableRegionSize =
+ r.getDimensionPixelSize(R.dimen.display_cutout_touchable_region_size)
+ val bounds = displayCutout.boundingRectsAll
+ if (bounds[DisplayCutout.BOUNDS_POSITION_LEFT] != null) {
+ mSwipeStartThreshold.left =
+ Math.max(
+ mSwipeStartThreshold.left,
+ bounds[DisplayCutout.BOUNDS_POSITION_LEFT]!!.width() +
+ mDisplayCutoutTouchableRegionSize
+ )
+ }
+ if (bounds[DisplayCutout.BOUNDS_POSITION_TOP] != null) {
+ mSwipeStartThreshold.top =
+ Math.max(
+ mSwipeStartThreshold.top,
+ bounds[DisplayCutout.BOUNDS_POSITION_TOP]!!.height() +
+ mDisplayCutoutTouchableRegionSize
+ )
+ }
+ if (bounds[DisplayCutout.BOUNDS_POSITION_RIGHT] != null) {
+ mSwipeStartThreshold.right =
+ Math.max(
+ mSwipeStartThreshold.right,
+ bounds[DisplayCutout.BOUNDS_POSITION_RIGHT]!!.width() +
+ mDisplayCutoutTouchableRegionSize
+ )
+ }
+ if (bounds[DisplayCutout.BOUNDS_POSITION_BOTTOM] != null) {
+ mSwipeStartThreshold.bottom =
+ Math.max(
+ mSwipeStartThreshold.bottom,
+ bounds[DisplayCutout.BOUNDS_POSITION_BOTTOM]!!.height() +
+ mDisplayCutoutTouchableRegionSize
+ )
+ }
+ }
+ if (DEBUG)
+ Log.d(
+ TAG,
+ "mSwipeStartThreshold=$mSwipeStartThreshold" +
+ " mSwipeDistanceThreshold=$mSwipeDistanceThreshold"
+ )
+ }
+
+ fun onInputEvent(ev: InputEvent) {
+ if (ev !is MotionEvent) {
+ return
+ }
+ if (DEBUG) Log.d(TAG, "Received motion event $ev")
+ if (ev.isTouchEvent) {
+ mFlingGestureDetector?.onTouchEvent(ev)
+ }
+ when (ev.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ mSwipeFireable = true
+ mDebugFireable = true
+ mDownPointers = 0
+ captureDown(ev, 0)
+ if (mMouseHoveringAtLeft) {
+ mMouseHoveringAtLeft = false
+ mCallbacks?.onMouseLeaveFromLeft()
+ }
+ if (mMouseHoveringAtTop) {
+ mMouseHoveringAtTop = false
+ mCallbacks?.onMouseLeaveFromTop()
+ }
+ if (mMouseHoveringAtRight) {
+ mMouseHoveringAtRight = false
+ mCallbacks?.onMouseLeaveFromRight()
+ }
+ if (mMouseHoveringAtBottom) {
+ mMouseHoveringAtBottom = false
+ mCallbacks?.onMouseLeaveFromBottom()
+ }
+ mCallbacks?.onDown()
+ }
+ MotionEvent.ACTION_POINTER_DOWN -> {
+ captureDown(ev, ev.actionIndex)
+ if (mDebugFireable) {
+ mDebugFireable = ev.pointerCount < 5
+ if (!mDebugFireable) {
+ if (DEBUG) Log.d(TAG, "Firing debug")
+ mCallbacks?.onDebug()
+ }
+ }
+ }
+ MotionEvent.ACTION_MOVE ->
+ if (mSwipeFireable) {
+ val trackpadSwipe = detectTrackpadThreeFingerSwipe(ev)
+ mSwipeFireable = trackpadSwipe == TRACKPAD_SWIPE_NONE
+ if (!mSwipeFireable) {
+ if (trackpadSwipe == TRACKPAD_SWIPE_FROM_TOP) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromTop from trackpad")
+ mCallbacks?.onSwipeFromTop()
+ } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_BOTTOM) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromBottom from trackpad")
+ mCallbacks?.onSwipeFromBottom()
+ } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_RIGHT) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromRight from trackpad")
+ mCallbacks?.onSwipeFromRight()
+ } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_LEFT) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromLeft from trackpad")
+ mCallbacks?.onSwipeFromLeft()
+ }
+ } else {
+ val swipe = detectSwipe(ev)
+ mSwipeFireable = swipe == SWIPE_NONE
+ if (swipe == SWIPE_FROM_TOP) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromTop")
+ mCallbacks?.onSwipeFromTop()
+ } else if (swipe == SWIPE_FROM_BOTTOM) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromBottom")
+ mCallbacks?.onSwipeFromBottom()
+ } else if (swipe == SWIPE_FROM_RIGHT) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromRight")
+ mCallbacks?.onSwipeFromRight()
+ } else if (swipe == SWIPE_FROM_LEFT) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromLeft")
+ mCallbacks?.onSwipeFromLeft()
+ }
+ }
+ }
+ MotionEvent.ACTION_HOVER_MOVE ->
+ if (ev.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ val eventX = ev.x
+ val eventY = ev.y
+ if (!mMouseHoveringAtLeft && eventX == 0f) {
+ mCallbacks?.onMouseHoverAtLeft()
+ mMouseHoveringAtLeft = true
+ } else if (mMouseHoveringAtLeft && eventX > 0) {
+ mCallbacks?.onMouseLeaveFromLeft()
+ mMouseHoveringAtLeft = false
+ }
+ if (!mMouseHoveringAtTop && eventY == 0f) {
+ mCallbacks?.onMouseHoverAtTop()
+ mMouseHoveringAtTop = true
+ } else if (mMouseHoveringAtTop && eventY > 0) {
+ mCallbacks?.onMouseLeaveFromTop()
+ mMouseHoveringAtTop = false
+ }
+ if (!mMouseHoveringAtRight && eventX >= screenWidth - 1) {
+ mCallbacks?.onMouseHoverAtRight()
+ mMouseHoveringAtRight = true
+ } else if (mMouseHoveringAtRight && eventX < screenWidth - 1) {
+ mCallbacks?.onMouseLeaveFromRight()
+ mMouseHoveringAtRight = false
+ }
+ if (!mMouseHoveringAtBottom && eventY >= screenHeight - 1) {
+ mCallbacks?.onMouseHoverAtBottom()
+ mMouseHoveringAtBottom = true
+ } else if (mMouseHoveringAtBottom && eventY < screenHeight - 1) {
+ mCallbacks?.onMouseLeaveFromBottom()
+ mMouseHoveringAtBottom = false
+ }
+ }
+ MotionEvent.ACTION_UP,
+ MotionEvent.ACTION_CANCEL -> {
+ mSwipeFireable = false
+ mDebugFireable = false
+ mCallbacks?.onUpOrCancel()
+ }
+ else -> if (DEBUG) Log.d(TAG, "Ignoring $ev")
+ }
+ }
+
+ fun setCallbacks(callbacks: Callbacks) {
+ mCallbacks = callbacks
+ }
+
+ private fun captureDown(event: MotionEvent, pointerIndex: Int) {
+ val pointerId = event.getPointerId(pointerIndex)
+ val i = findIndex(pointerId)
+ if (DEBUG) Log.d(TAG, "pointer $pointerId down pointerIndex=$pointerIndex trackingIndex=$i")
+ if (i != UNTRACKED_POINTER) {
+ mDownX[i] = event.getX(pointerIndex)
+ mDownY[i] = event.getY(pointerIndex)
+ mDownTime[i] = event.eventTime
+ if (DEBUG)
+ Log.d(TAG, "pointer " + pointerId + " down x=" + mDownX[i] + " y=" + mDownY[i])
+ }
+ }
+
+ protected fun currentGestureStartedInRegion(r: Region): Boolean {
+ return r.contains(mDownX[0].toInt(), mDownY[0].toInt())
+ }
+
+ private fun findIndex(pointerId: Int): Int {
+ for (i in 0 until mDownPointers) {
+ if (mDownPointerId[i] == pointerId) {
+ return i
+ }
+ }
+ if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
+ return UNTRACKED_POINTER
+ }
+ mDownPointerId[mDownPointers++] = pointerId
+ return mDownPointers - 1
+ }
+
+ private fun detectTrackpadThreeFingerSwipe(move: MotionEvent): Int {
+ if (!isTrackpadThreeFingerSwipe(move)) {
+ return TRACKPAD_SWIPE_NONE
+ }
+ val dx = move.x - mDownX[0]
+ val dy = move.y - mDownY[0]
+ if (Math.abs(dx) < Math.abs(dy)) {
+ if (Math.abs(dy) > mSwipeDistanceThreshold) {
+ return if (dy > 0) TRACKPAD_SWIPE_FROM_TOP else TRACKPAD_SWIPE_FROM_BOTTOM
+ }
+ } else {
+ if (Math.abs(dx) > mSwipeDistanceThreshold) {
+ return if (dx > 0) TRACKPAD_SWIPE_FROM_LEFT else TRACKPAD_SWIPE_FROM_RIGHT
+ }
+ }
+ return TRACKPAD_SWIPE_NONE
+ }
+
+ private fun isTrackpadThreeFingerSwipe(event: MotionEvent): Boolean {
+ return (event.classification == CLASSIFICATION_MULTI_FINGER_SWIPE &&
+ event.getAxisValue(AXIS_GESTURE_SWIPE_FINGER_COUNT) == 3f)
+ }
+ private fun detectSwipe(move: MotionEvent): Int {
+ val historySize = move.historySize
+ val pointerCount = move.pointerCount
+ for (p in 0 until pointerCount) {
+ val pointerId = move.getPointerId(p)
+ val i = findIndex(pointerId)
+ if (i != UNTRACKED_POINTER) {
+ for (h in 0 until historySize) {
+ val time = move.getHistoricalEventTime(h)
+ val x = move.getHistoricalX(p, h)
+ val y = move.getHistoricalY(p, h)
+ val swipe = detectSwipe(i, time, x, y)
+ if (swipe != SWIPE_NONE) {
+ return swipe
+ }
+ }
+ val swipe = detectSwipe(i, move.eventTime, move.getX(p), move.getY(p))
+ if (swipe != SWIPE_NONE) {
+ return swipe
+ }
+ }
+ }
+ return SWIPE_NONE
+ }
+
+ private fun detectSwipe(i: Int, time: Long, x: Float, y: Float): Int {
+ val fromX = mDownX[i]
+ val fromY = mDownY[i]
+ val elapsed = time - mDownTime[i]
+ if (DEBUG)
+ Log.d(
+ TAG,
+ "pointer " +
+ mDownPointerId[i] +
+ " moved (" +
+ fromX +
+ "->" +
+ x +
+ "," +
+ fromY +
+ "->" +
+ y +
+ ") in " +
+ elapsed
+ )
+ if (
+ fromY <= mSwipeStartThreshold.top &&
+ y > fromY + mSwipeDistanceThreshold &&
+ elapsed < SWIPE_TIMEOUT_MS
+ ) {
+ return SWIPE_FROM_TOP
+ }
+ if (
+ fromY >= screenHeight - mSwipeStartThreshold.bottom &&
+ y < fromY - mSwipeDistanceThreshold &&
+ elapsed < SWIPE_TIMEOUT_MS
+ ) {
+ return SWIPE_FROM_BOTTOM
+ }
+ if (
+ fromX >= screenWidth - mSwipeStartThreshold.right &&
+ x < fromX - mSwipeDistanceThreshold &&
+ elapsed < SWIPE_TIMEOUT_MS
+ ) {
+ return SWIPE_FROM_RIGHT
+ }
+ return if (
+ fromX <= mSwipeStartThreshold.left &&
+ x > fromX + mSwipeDistanceThreshold &&
+ elapsed < SWIPE_TIMEOUT_MS
+ ) {
+ SWIPE_FROM_LEFT
+ } else SWIPE_NONE
+ }
+
+ fun dump(pw: PrintWriter, prefix: String) {
+ val inner = "$prefix "
+ pw.println(prefix + TAG + ":")
+ pw.print(inner)
+ pw.print("mDisplayCutoutTouchableRegionSize=")
+ pw.println(mDisplayCutoutTouchableRegionSize)
+ pw.print(inner)
+ pw.print("mSwipeStartThreshold=")
+ pw.println(mSwipeStartThreshold)
+ pw.print(inner)
+ pw.print("mSwipeDistanceThreshold=")
+ pw.println(mSwipeDistanceThreshold)
+ }
+
+ private inner class FlingGestureDetector internal constructor() :
+ GestureDetector.SimpleOnGestureListener() {
+ private val mOverscroller: OverScroller = OverScroller(mContext)
+
+ override fun onSingleTapUp(e: MotionEvent): Boolean {
+ if (!mOverscroller.isFinished) {
+ mOverscroller.forceFinished(true)
+ }
+ return true
+ }
+
+ override fun onFling(
+ down: MotionEvent?,
+ up: MotionEvent,
+ velocityX: Float,
+ velocityY: Float
+ ): Boolean {
+ mOverscroller.computeScrollOffset()
+ val now = SystemClock.uptimeMillis()
+ if (mLastFlingTime != 0L && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
+ mOverscroller.forceFinished(true)
+ }
+ mOverscroller.fling(
+ 0,
+ 0,
+ velocityX.toInt(),
+ velocityY.toInt(),
+ Int.MIN_VALUE,
+ Int.MAX_VALUE,
+ Int.MIN_VALUE,
+ Int.MAX_VALUE
+ )
+ var duration = mOverscroller.duration
+ if (duration > MAX_FLING_TIME_MILLIS) {
+ duration = MAX_FLING_TIME_MILLIS
+ }
+ mLastFlingTime = now
+ mCallbacks?.onFling(duration)
+ return true
+ }
+ }
+
+ interface Callbacks {
+ fun onSwipeFromTop()
+ fun onSwipeFromBottom()
+ fun onSwipeFromRight()
+ fun onSwipeFromLeft()
+ fun onFling(durationMs: Int)
+ fun onDown()
+ fun onUpOrCancel()
+ fun onMouseHoverAtLeft()
+ fun onMouseHoverAtTop()
+ fun onMouseHoverAtRight()
+ fun onMouseHoverAtBottom()
+ fun onMouseLeaveFromLeft()
+ fun onMouseLeaveFromTop()
+ fun onMouseLeaveFromRight()
+ fun onMouseLeaveFromBottom()
+ fun onDebug()
+ }
+
+ companion object {
+ private const val TAG = "GesturePointerEventHandler"
+ private const val DEBUG = false
+ private const val SWIPE_TIMEOUT_MS: Long = 500
+ private const val MAX_TRACKED_POINTERS = 32 // max per input system
+ private const val UNTRACKED_POINTER = -1
+ private const val MAX_FLING_TIME_MILLIS = 5000
+ private const val SWIPE_NONE = 0
+ private const val SWIPE_FROM_TOP = 1
+ private const val SWIPE_FROM_BOTTOM = 2
+ private const val SWIPE_FROM_RIGHT = 3
+ private const val SWIPE_FROM_LEFT = 4
+ private const val TRACKPAD_SWIPE_NONE = 0
+ private const val TRACKPAD_SWIPE_FROM_TOP = 1
+ private const val TRACKPAD_SWIPE_FROM_BOTTOM = 2
+ private const val TRACKPAD_SWIPE_FROM_RIGHT = 3
+ private const val TRACKPAD_SWIPE_FROM_LEFT = 4
+
+ private fun <T> checkNull(name: String, arg: T?): T {
+ requireNotNull(arg) { "$name must not be null" }
+ return arg
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 62a0d13..5c2f9a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -39,7 +39,10 @@
override fun attach(pipeline: NotifPipeline) {
pipeline.addOnAfterRenderListListener(::onAfterRenderList)
- groupExpansionManagerImpl.attach(pipeline)
+ // TODO(b/282865576): This has an issue where it makes changes to some groups without
+ // notifying listeners. To be fixed in QPR, but for now let's comment it out to avoid the
+ // group expansion bug.
+ // groupExpansionManagerImpl.attach(pipeline)
}
fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index 5d33804..46af03a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -67,29 +67,18 @@
* Cleanup entries from mExpandedGroups that no longer exist in the pipeline.
*/
private final OnBeforeRenderListListener mNotifTracker = (entries) -> {
- if (mExpandedGroups.isEmpty()) {
- return; // nothing to do
- }
-
final Set<NotificationEntry> renderingSummaries = new HashSet<>();
for (ListEntry entry : entries) {
if (entry instanceof GroupEntry) {
renderingSummaries.add(entry.getRepresentativeEntry());
}
}
-
- // Create a copy of mExpandedGroups so we can modify it in a thread-safe way.
- final var currentExpandedGroups = new HashSet<>(mExpandedGroups);
- for (NotificationEntry entry : currentExpandedGroups) {
- setExpanded(entry, renderingSummaries.contains(entry));
- }
+ mExpandedGroups.removeIf(expandedGroup -> !renderingSummaries.contains(expandedGroup));
};
public void attach(NotifPipeline pipeline) {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
- mDumpManager.registerDumpable(this);
- pipeline.addOnBeforeRenderListListener(mNotifTracker);
- }
+ mDumpManager.registerDumpable(this);
+ pipeline.addOnBeforeRenderListListener(mNotifTracker);
}
@Override
@@ -105,24 +94,11 @@
@Override
public void setGroupExpanded(NotificationEntry entry, boolean expanded) {
final NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry);
- setExpanded(groupSummary, expanded);
- }
-
- /**
- * Add or remove {@code entry} to/from {@code mExpandedGroups} and notify listeners if
- * something changed. This assumes that {@code entry} is a group summary.
- * <p>
- * TODO(b/293434635): Currently, in spite of its docs,
- * {@code mGroupMembershipManager.getGroupSummary(entry)} returns null if {@code entry} is
- * already a summary. Instead of needing this helper method to bypass that, we probably want to
- * move this code back to {@code setGroupExpanded} and use that everywhere.
- */
- private void setExpanded(NotificationEntry entry, boolean expanded) {
boolean changed;
if (expanded) {
- changed = mExpandedGroups.add(entry);
+ changed = mExpandedGroups.add(groupSummary);
} else {
- changed = mExpandedGroups.remove(entry);
+ changed = mExpandedGroups.remove(groupSummary);
}
// Only notify listeners if something changed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt
index 4429939..d10b556 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt
@@ -21,29 +21,25 @@
import android.util.Log
import android.view.LayoutInflater
import android.view.View
-import com.android.systemui.Dumpable
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
import com.android.systemui.statusbar.notification.row.NotificationRowModule.NOTIF_REMOTEVIEWS_FACTORIES
-import com.android.systemui.util.asIndenting
-import com.android.systemui.util.withIncreasedIndent
-import java.io.PrintWriter
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import javax.inject.Named
/**
* Implementation of [NotifLayoutInflaterFactory]. This class uses a set of
* [NotifRemoteViewsFactory] objects to create replacement views for Notification RemoteViews.
*/
-open class NotifLayoutInflaterFactory
-@Inject
+class NotifLayoutInflaterFactory
+@AssistedInject
constructor(
- dumpManager: DumpManager,
+ @Assisted private val row: ExpandableNotificationRow,
+ @Assisted @InflationFlag val layoutType: Int,
@Named(NOTIF_REMOTEVIEWS_FACTORIES)
private val remoteViewsFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory>
-) : LayoutInflater.Factory2, Dumpable {
- init {
- dumpManager.registerNormalDumpable(TAG, this)
- }
+) : LayoutInflater.Factory2 {
override fun onCreateView(
parent: View?,
@@ -51,41 +47,32 @@
context: Context,
attrs: AttributeSet
): View? {
- var view: View? = null
var handledFactory: NotifRemoteViewsFactory? = null
+ var result: View? = null
for (layoutFactory in remoteViewsFactories) {
- view = layoutFactory.instantiate(parent, name, context, attrs)
- if (view != null) {
+ layoutFactory.instantiate(row, layoutType, parent, name, context, attrs)?.run {
check(handledFactory == null) {
- "${layoutFactory.javaClass.name} tries to produce view. However, " +
- "${handledFactory?.javaClass?.name} produced view for $name before."
+ "$layoutFactory tries to produce name:$name with type:$layoutType. " +
+ "However, $handledFactory produced view for $name before."
}
handledFactory = layoutFactory
+ result = this
}
}
- logOnCreateView(name, view, handledFactory)
- return view
+ logOnCreateView(name, result, handledFactory)
+ return result
}
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? =
onCreateView(null, name, context, attrs)
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- val indentingPW = pw.asIndenting()
-
- indentingPW.appendLine("$TAG ReplacementFactories:")
- indentingPW.withIncreasedIndent {
- remoteViewsFactories.forEach { indentingPW.appendLine(it.javaClass.simpleName) }
- }
- }
-
private fun logOnCreateView(
name: String,
replacedView: View?,
factory: NotifRemoteViewsFactory?
) {
if (SPEW && replacedView != null && factory != null) {
- Log.d(TAG, "$factory produced view for $name: $replacedView")
+ Log.d(TAG, "$factory produced $replacedView for name:$name with type:$layoutType")
}
}
@@ -93,4 +80,12 @@
private const val TAG = "NotifLayoutInflaterFac"
private val SPEW = Log.isLoggable(TAG, Log.VERBOSE)
}
+
+ @AssistedFactory
+ interface Provider {
+ fun provide(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int
+ ): NotifLayoutInflaterFactory
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt
index eebd4d4..bf740e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt
@@ -19,10 +19,18 @@
import android.content.Context
import android.util.AttributeSet
import android.view.View
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
/** Interface used to create replacement view instances in Notification RemoteViews. */
interface NotifRemoteViewsFactory {
/** return the replacement view instance for the given view name */
- fun instantiate(parent: View?, name: String, context: Context, attrs: AttributeSet): View?
+ fun instantiate(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int,
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet
+ ): View?
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 0ad77bd..86f545d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -79,7 +79,7 @@
private final ConversationNotificationProcessor mConversationProcessor;
private final Executor mBgExecutor;
private final SmartReplyStateInflater mSmartReplyStateInflater;
- private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
+ private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
@Inject
NotificationContentInflater(
@@ -89,14 +89,14 @@
MediaFeatureFlag mediaFeatureFlag,
@Background Executor bgExecutor,
SmartReplyStateInflater smartRepliesInflater,
- NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
+ NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
mRemoteViewCache = remoteViewCache;
mRemoteInputManager = remoteInputManager;
mConversationProcessor = conversationProcessor;
mIsMediaInQS = mediaFeatureFlag.getEnabled();
mBgExecutor = bgExecutor;
mSmartReplyStateInflater = smartRepliesInflater;
- mNotifLayoutInflaterFactory = notifLayoutInflaterFactory;
+ mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
}
@Override
@@ -141,7 +141,7 @@
mRemoteInputManager.getRemoteViewsOnClickHandler(),
mIsMediaInQS,
mSmartReplyStateInflater,
- mNotifLayoutInflaterFactory);
+ mNotifLayoutInflaterFactoryProvider);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
@@ -165,7 +165,8 @@
bindParams.usesIncreasedHeight,
bindParams.usesIncreasedHeadsUpHeight,
packageContext,
- mNotifLayoutInflaterFactory);
+ row,
+ mNotifLayoutInflaterFactoryProvider);
result = inflateSmartReplyViews(result, reInflateFlags, entry,
row.getContext(), packageContext,
@@ -304,7 +305,8 @@
private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
Notification.Builder builder, boolean isLowPriority, boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight, Context packageContext,
- NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
+ ExpandableNotificationRow row,
+ NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
InflationProgress result = new InflationProgress();
if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
@@ -322,7 +324,7 @@
if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
result.newPublicView = builder.makePublicContentView(isLowPriority);
}
- setNotifsViewsInflaterFactory(result, notifLayoutInflaterFactory);
+ setNotifsViewsInflaterFactory(result, row, notifLayoutInflaterFactoryProvider);
result.packageContext = packageContext;
result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
@@ -331,12 +333,16 @@
}
private static void setNotifsViewsInflaterFactory(InflationProgress result,
- NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
- setRemoteViewsInflaterFactory(result.newContentView, notifLayoutInflaterFactory);
+ ExpandableNotificationRow row,
+ NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
+ setRemoteViewsInflaterFactory(result.newContentView,
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_CONTRACTED));
setRemoteViewsInflaterFactory(result.newExpandedView,
- notifLayoutInflaterFactory);
- setRemoteViewsInflaterFactory(result.newHeadsUpView, notifLayoutInflaterFactory);
- setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactory);
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_EXPANDED));
+ setRemoteViewsInflaterFactory(result.newHeadsUpView,
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP));
+ setRemoteViewsInflaterFactory(result.newPublicView,
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC));
}
private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews,
@@ -821,7 +827,7 @@
private final ConversationNotificationProcessor mConversationProcessor;
private final boolean mIsMediaInQS;
private final SmartReplyStateInflater mSmartRepliesInflater;
- private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
+ private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
private AsyncInflationTask(
Executor bgExecutor,
@@ -838,7 +844,7 @@
RemoteViews.InteractionHandler remoteViewClickHandler,
boolean isMediaFlagEnabled,
SmartReplyStateInflater smartRepliesInflater,
- NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
+ NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
mEntry = entry;
mRow = row;
mBgExecutor = bgExecutor;
@@ -854,7 +860,7 @@
mCallback = callback;
mConversationProcessor = conversationProcessor;
mIsMediaInQS = isMediaFlagEnabled;
- mNotifLayoutInflaterFactory = notifLayoutInflaterFactory;
+ mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
entry.setInflationTask(this);
}
@@ -898,8 +904,8 @@
}
InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight,
- mUsesIncreasedHeadsUpHeight, packageContext,
- mNotifLayoutInflaterFactory);
+ mUsesIncreasedHeadsUpHeight, packageContext, mRow,
+ mNotifLayoutInflaterFactoryProvider);
InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState();
InflationProgress result = inflateSmartReplyViews(
inflationProgress,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
index 51e4537..4ace194 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
@@ -21,6 +21,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerExecutor;
+import android.os.Trace;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
@@ -69,6 +70,7 @@
mContentObserver = new ContentObserver(mBackgroundHandler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".ContentObserver.onChange");
super.onChange(selfChange, uri);
synchronized (mListeners) {
if (mListeners.containsKey(uri)) {
@@ -79,12 +81,15 @@
}
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_APP);
}
};
mCurrentUserTrackerCallback = new UserTracker.Callback() {
@Override
public void onUserChanged(int newUser, Context userContext) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".UserTracker.Callback.onUserChanged");
+
synchronized (mListeners) {
if (mListeners.size() > 0) {
mSecureSettings.unregisterContentObserver(mContentObserver);
@@ -94,6 +99,7 @@
}
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_APP);
}
};
mUserTracker.addCallback(
@@ -113,6 +119,7 @@
if (uri == null || listener == null) {
return;
}
+ Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".addCallback");
synchronized (mListeners) {
ArrayList<Listener> currentListeners = mListeners.get(uri);
if (currentListeners == null) {
@@ -132,10 +139,12 @@
String value = getCurrentSettingValue(uri, userId);
mMainHandler.post(() -> listener.onSettingChanged(uri, userId, value));
});
-
+ Trace.traceEnd(Trace.TRACE_TAG_APP);
}
public void removeCallback(Uri uri, Listener listener) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".removeCallback");
+
synchronized (mListeners) {
ArrayList<Listener> currentListeners = mListeners.get(uri);
@@ -150,10 +159,13 @@
mSecureSettings.unregisterContentObserver(mContentObserver);
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_APP);
}
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".dump");
+
synchronized (mListeners) {
pw.println("Settings Uri Listener List:");
for (Uri uri : mListeners.keySet()) {
@@ -163,6 +175,7 @@
}
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_APP);
}
private String getCurrentSettingValue(Uri uri, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
index c276827..96547db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
@@ -23,10 +23,13 @@
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.ImageFloatingTextView
import com.android.internal.widget.MessagingLayout
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
import javax.inject.Inject
class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory {
override fun instantiate(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int,
parent: View?,
name: String,
context: Context,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ea57eb4..27b8406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -92,6 +92,8 @@
import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -101,7 +103,6 @@
import javax.inject.Inject;
-import dagger.Lazy;
import kotlinx.coroutines.CoroutineDispatcher;
/**
@@ -450,14 +451,6 @@
}
}
- private KeyguardStateController.Callback mKeyguardStateControllerCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onUnlockedChanged() {
- updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide());
- }
- };
-
private void registerListeners() {
mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
mStatusBarStateController.addCallback(this);
@@ -471,7 +464,6 @@
mDockManager.addListener(mDockEventListener);
mIsDocked = mDockManager.isDocked();
}
- mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
mShadeViewController.postToView(() ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index cd1afc7..37f032b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -226,7 +226,7 @@
val builder = InteractionJankMonitor.Configuration.Builder
.withView(
InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD,
- notifShadeWindowControllerLazy.get().windowRootView
+ checkNotNull(notifShadeWindowControllerLazy.get().windowRootView)
)
.setTag(statusBarStateControllerImpl.getClockId())
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
index 2325acf..f5decaa 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
@@ -17,11 +17,13 @@
package com.android.systemui.unfold
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FoldProvider
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener
import java.util.concurrent.Executor
-class UnfoldProgressProvider(private val unfoldProgressProvider: UnfoldTransitionProgressProvider) :
+class UnfoldProgressProvider(private val unfoldProgressProvider: UnfoldTransitionProgressProvider,
+ private val foldProvider: FoldProvider) :
ShellUnfoldProgressProvider {
override fun addListener(executor: Executor, listener: UnfoldListener) {
@@ -39,5 +41,11 @@
executor.execute { listener.onStateChangeFinished() }
}
})
+
+ foldProvider.registerCallback(object : FoldProvider.FoldCallback {
+ override fun onFoldUpdated(isFolded: Boolean) {
+ listener.onFoldStateChanged(isFolded)
+ }
+ }, executor)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 992b022..ed3eacd 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -118,6 +118,7 @@
@Singleton
fun provideShellProgressProvider(
config: UnfoldTransitionConfig,
+ foldProvider: FoldProvider,
provider: Provider<Optional<UnfoldTransitionProgressProvider>>,
@Named(UNFOLD_ONLY_PROVIDER)
unfoldOnlyProvider: Provider<Optional<UnfoldTransitionProgressProvider>>
@@ -135,8 +136,9 @@
null
}
- return resultingProvider?.get()?.orElse(null)?.let(::UnfoldProgressProvider)
- ?: ShellUnfoldProgressProvider.NO_PROVIDER
+ return resultingProvider?.get()?.orElse(null)?.let {
+ unfoldProgressProvider -> UnfoldProgressProvider(unfoldProgressProvider, foldProvider)
+ } ?: ShellUnfoldProgressProvider.NO_PROVIDER
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 954765c..8cfa555 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -31,7 +31,6 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.data.model.SelectedUserModel
import com.android.systemui.user.data.model.SelectionStatus
@@ -49,7 +48,6 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
@@ -77,9 +75,6 @@
/** [UserInfo] of the currently-selected user. */
val selectedUserInfo: Flow<UserInfo>
- /** Whether user switching is currently in progress. */
- val userSwitchingInProgress: Flow<Boolean>
-
/** User ID of the main user. */
val mainUserId: Int
@@ -162,10 +157,6 @@
private var _isGuestUserResetting: Boolean = false
override var isGuestUserResetting: Boolean = _isGuestUserResetting
- private val _isUserSwitchingInProgress = MutableStateFlow(false)
- override val userSwitchingInProgress: Flow<Boolean>
- get() = _isUserSwitchingInProgress
-
override val isGuestUserCreationScheduled = AtomicBoolean()
override val isStatusBarUserChipEnabled: Boolean =
@@ -175,12 +166,6 @@
override var isRefreshUsersPaused: Boolean = false
- init {
- if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) {
- observeUserSwitching()
- }
- }
-
override val selectedUser: StateFlow<SelectedUserModel> = run {
// Some callbacks don't modify the selection status, so maintain the current value.
var currentSelectionStatus = SelectionStatus.SELECTION_COMPLETE
@@ -259,28 +244,6 @@
return _userSwitcherSettings.value.isUserSwitcherEnabled
}
- private fun observeUserSwitching() {
- conflatedCallbackFlow {
- val callback =
- object : UserTracker.Callback {
- override fun onUserChanging(newUser: Int, userContext: Context) {
- trySendWithFailureLogging(true, TAG, "userSwitching started")
- }
-
- override fun onUserChanged(newUserId: Int, userContext: Context) {
- trySendWithFailureLogging(false, TAG, "userSwitching completed")
- }
- }
- tracker.addCallback(callback, mainDispatcher.asExecutor())
- trySendWithFailureLogging(false, TAG, "initial value defaulting to false")
- awaitClose { tracker.removeCallback(callback) }
- }
- .onEach { _isUserSwitchingInProgress.value = it }
- // TODO (b/262838215), Make this stateIn and initialize directly in field declaration
- // once the flag is launched
- .launchIn(applicationScope)
- }
-
private suspend fun getSettings(): UserSwitcherSettingsModel {
return withContext(backgroundDispatcher) {
val isSimpleUserSwitcher =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
index fcc4040..0da7b4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
@@ -17,14 +17,19 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE
+import android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT
+import android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED
import android.hardware.biometrics.SensorProperties
import android.hardware.face.FaceManager
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.shared.model.LockoutMode
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -74,18 +79,52 @@
@Test
fun providesTheValuePassedToTheAuthenticatorsRegisteredCallback() {
testScope.runTest {
- val sensor by collectLastValue(underTest.sensorInfo)
runCurrent()
verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
callback.value.onAllAuthenticatorsRegistered(
listOf(createSensorProperties(1, SensorProperties.STRENGTH_STRONG))
)
+ runCurrent()
+ val sensor by collectLastValue(underTest.sensorInfo)
assertThat(sensor).isEqualTo(FaceSensorInfo(1, SensorStrength.STRONG))
}
}
+ @Test
+ fun providesTheNoneLockoutModeWhenFaceManagerIsNotAvailable() =
+ testScope.runTest {
+ underTest = createRepository(null)
+
+ assertThat(underTest.getLockoutMode(-1)).isEqualTo(LockoutMode.NONE)
+ }
+
+ @Test
+ fun providesTheLockoutModeFromFaceManager() =
+ testScope.runTest {
+ val sensorId = 99
+ val userId = 999
+ runCurrent()
+ verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
+ callback.value.onAllAuthenticatorsRegistered(
+ listOf(createSensorProperties(sensorId, SensorProperties.STRENGTH_STRONG))
+ )
+ runCurrent()
+
+ whenever(faceManager.getLockoutModeForUser(sensorId, userId))
+ .thenReturn(BIOMETRIC_LOCKOUT_TIMED)
+ assertThat(underTest.getLockoutMode(userId)).isEqualTo(LockoutMode.TIMED)
+
+ whenever(faceManager.getLockoutModeForUser(sensorId, userId))
+ .thenReturn(BIOMETRIC_LOCKOUT_PERMANENT)
+ assertThat(underTest.getLockoutMode(userId)).isEqualTo(LockoutMode.PERMANENT)
+
+ whenever(faceManager.getLockoutModeForUser(sensorId, userId))
+ .thenReturn(BIOMETRIC_LOCKOUT_NONE)
+ assertThat(underTest.getLockoutMode(userId)).isEqualTo(LockoutMode.NONE)
+ }
+
private fun createSensorProperties(id: Int, strength: Int) =
FaceSensorPropertiesInternal(id, strength, 0, emptyList(), 1, false, false, false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
index da55d5a..95b72d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
@@ -28,7 +28,7 @@
@SmallTest
@RunWith(Parameterized::class)
class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
- val underTest = BoundingBoxOverlapDetector()
+ val underTest = BoundingBoxOverlapDetector(1f)
@Test
fun isGoodOverlap() {
@@ -83,7 +83,7 @@
GESTURE_START
)
-private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 400 /* bottom */)
private val OVERLAY = Rect(0 /* left */, 100 /* top */, 400 /* right */, 600 /* bottom */)
private fun genTestCases(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 11b0b07..47084c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -312,10 +312,13 @@
assertThat(message).isEqualTo(PromptMessage.Empty)
assertThat(messageVisible).isFalse()
}
+ val clearIconError = !restart
assertThat(legacyState)
.isEqualTo(
if (restart) {
AuthBiometricView.STATE_AUTHENTICATING
+ } else if (clearIconError) {
+ AuthBiometricView.STATE_IDLE
} else {
AuthBiometricView.STATE_HELP
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 8236165..d4bba72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -29,6 +29,8 @@
import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
@@ -156,4 +158,24 @@
assertThat(isShowing).isEqualTo(false)
job.cancel()
}
+
+ @Test
+ fun keyguardPosition_noValueSet_emptyByDefault() = runTest {
+ val positionValues by collectValues(underTest.keyguardPosition)
+
+ runCurrent()
+
+ assertThat(positionValues).isEmpty()
+ }
+
+ @Test
+ fun keyguardPosition_valueSet_returnsValue() = runTest {
+ val position by collectLastValue(underTest.keyguardPosition)
+ runCurrent()
+
+ repository.setKeyguardPosition(123f)
+ runCurrent()
+
+ assertThat(position).isEqualTo(123f)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 64b9470..f0dbaf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -71,6 +71,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.FakeKeyguardStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.captureMany
@@ -295,6 +296,7 @@
fun faceAuthDoesNotRunWhileItIsAlreadyRunning() =
testScope.runTest {
initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
@@ -311,6 +313,7 @@
testScope.runTest {
initCollectors()
verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
+ allPreconditionsToRunFaceAuthAreTrue()
underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
@@ -364,6 +367,7 @@
fun cancelStopsFaceAuthentication() =
testScope.runTest {
initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
@@ -417,6 +421,7 @@
fun faceAuthShouldWaitAndRunIfTriggeredWhileCancelling() =
testScope.runTest {
initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
@@ -492,6 +497,7 @@
)
underTest = createDeviceEntryFaceAuthRepositoryImpl()
initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
@@ -803,7 +809,10 @@
assertThat(authenticated()).isTrue()
- fakeUserRepository.setUserSwitching(true)
+ fakeUserRepository.setSelectedUserInfo(
+ primaryUser,
+ SelectionStatus.SELECTION_IN_PROGRESS
+ )
assertThat(authenticated()).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 93f208e..ec11573 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.content.pm.UserInfo
import android.hardware.biometrics.BiometricFaceConstants
import android.os.Handler
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -25,6 +26,8 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.biometrics.shared.model.LockoutMode
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -48,6 +51,7 @@
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -76,6 +80,7 @@
private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
private lateinit var faceAuthRepository: FakeDeviceEntryFaceAuthRepository
private lateinit var fakeUserRepository: FakeUserRepository
+ private lateinit var facePropertyRepository: FakeFacePropertyRepository
private lateinit var fakeDeviceEntryFingerprintAuthRepository:
FakeDeviceEntryFingerprintAuthRepository
@@ -101,6 +106,8 @@
fakeDeviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
fakeUserRepository = FakeUserRepository()
+ fakeUserRepository.setUserInfos(listOf(primaryUser, secondaryUser))
+ facePropertyRepository = FakeFacePropertyRepository()
underTest =
SystemUIKeyguardFaceAuthInteractor(
mContext,
@@ -136,6 +143,7 @@
keyguardUpdateMonitor,
fakeDeviceEntryFingerprintAuthRepository,
fakeUserRepository,
+ facePropertyRepository,
)
}
@@ -220,9 +228,12 @@
testScope.runTest {
underTest.start()
- fakeUserRepository.setUserSwitching(false)
+ fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
runCurrent()
- fakeUserRepository.setUserSwitching(true)
+ fakeUserRepository.setSelectedUserInfo(
+ secondaryUser,
+ SelectionStatus.SELECTION_IN_PROGRESS
+ )
runCurrent()
assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
@@ -234,17 +245,27 @@
underTest.start()
// previously running
- fakeUserRepository.setUserSwitching(true)
+ fakeUserRepository.setSelectedUserInfo(
+ primaryUser,
+ SelectionStatus.SELECTION_IN_PROGRESS
+ )
runCurrent()
- fakeUserRepository.setUserSwitching(false)
+ bouncerRepository.setPrimaryShow(true)
+
+ facePropertyRepository.setLockoutMode(secondaryUser.id, LockoutMode.TIMED)
+ fakeUserRepository.setSelectedUserInfo(
+ secondaryUser,
+ SelectionStatus.SELECTION_COMPLETE
+ )
runCurrent()
assertThat(faceAuthRepository.isFaceAuthPaused()).isFalse()
+ assertThat(faceAuthRepository.wasDisabled).isTrue()
runCurrent()
assertThat(faceAuthRepository.runningAuthRequest.value!!.first)
.isEqualTo(FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING)
- assertThat(faceAuthRepository.runningAuthRequest.value!!.second).isEqualTo(true)
+ assertThat(faceAuthRepository.runningAuthRequest.value!!.second).isEqualTo(false)
}
@Test
@@ -259,7 +280,7 @@
runCurrent()
assertThat(faceAuthRepository.runningAuthRequest.value)
- .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, true))
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, false))
}
@Test
@@ -387,4 +408,11 @@
assertThat(faceAuthRepository.wasDisabled).isTrue()
}
+
+ companion object {
+ private const val primaryUserId = 1
+ private val primaryUser = UserInfo(primaryUserId, "test user", UserInfo.FLAG_PRIMARY)
+
+ private val secondaryUser = UserInfo(2, "secondary user", 0)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index e0ae0c3..a3f7fc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -131,12 +131,14 @@
}
@Test
- fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+ fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() {
keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU)
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
@@ -145,48 +147,42 @@
whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_doNothing() {
+ fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() {
keyguardInteractorWithDependencies.repository.setWakefulnessModel(asleepWakefulnessMode)
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionsDoNothing(KeyEvent.KEYCODE_MENU)
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+ fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() {
keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE)
- }
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
- @Test
- fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() {
- keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE)
- }
-
- @Test
- fun dispatchKeyEvent_enterActionUp_interactiveKeyguard_showsPrimaryBouncer() {
- keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
- }
-
- @Test
- fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
- keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
@@ -256,42 +252,4 @@
.isFalse()
verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
}
-
- private fun verifyActionUpCollapsesTheShade(keycode: Int) {
- // action down: does NOT collapse the shade
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
-
- // action up: collapses the shade
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
- verify(shadeController).animateCollapseShadeForced()
- }
-
- private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) {
- // action down: does NOT collapse the shade
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
- // action up: collapses the shade
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
- verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
- }
-
- private fun verifyActionsDoNothing(keycode: Int) {
- // action down: does nothing
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
- // action up: doesNothing
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 23f243c..a9f288d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -17,10 +17,8 @@
package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -48,7 +46,6 @@
private val underTest =
LockscreenSceneViewModel(
- applicationScope = testScope.backgroundScope,
authenticationInteractor = authenticationInteractor,
bouncerInteractor =
utils.bouncerInteractor(
@@ -58,32 +55,6 @@
)
@Test
- fun lockButtonIcon_whenLocked() =
- testScope.runTest {
- val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(false)
-
- assertThat((lockButtonIcon as? Icon.Resource)?.res)
- .isEqualTo(R.drawable.ic_device_lock_on)
- }
-
- @Test
- fun lockButtonIcon_whenUnlocked() =
- testScope.runTest {
- val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(true)
-
- assertThat((lockButtonIcon as? Icon.Resource)?.res)
- .isEqualTo(R.drawable.ic_device_lock_off)
- }
-
- @Test
fun upTransitionSceneKey_canSwipeToUnlock_gone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
@@ -120,32 +91,6 @@
}
@Test
- fun onContentClicked_deviceUnlocked_switchesToGone() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(true)
- runCurrent()
-
- underTest.onContentClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
- @Test
- fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- runCurrent()
-
- underTest.onContentClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- }
-
- @Test
fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 49ece66..ef07fab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -31,7 +31,6 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.model.SysUiState
import com.android.systemui.navigationbar.NavigationBarController
@@ -84,7 +83,6 @@
private val fakeSystemClock = FakeSystemClock()
private val sysUiState = SysUiState(displayTracker)
private val featureFlags = FakeFeatureFlags()
- private val screenLifecycle = ScreenLifecycle(dumpManager)
private val wakefulnessLifecycle =
WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager)
@@ -142,7 +140,6 @@
sysUiState,
mock(),
userTracker,
- screenLifecycle,
wakefulnessLifecycle,
uiEventLogger,
displayTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 53c04cc..46cbfac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -116,7 +116,6 @@
private val lockscreenSceneViewModel =
LockscreenSceneViewModel(
- applicationScope = testScope.backgroundScope,
authenticationInteractor = authenticationInteractor,
bouncerInteractor = bouncerInteractor,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
index 2b39354..d75405f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
@@ -20,6 +20,7 @@
import android.view.MotionEvent
import android.widget.SeekBar
import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.settingslib.RestrictedLockUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
@@ -39,6 +40,7 @@
import org.mockito.Mockito.isNull
import org.mockito.Mockito.never
import org.mockito.Mockito.notNull
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@@ -64,6 +66,7 @@
private lateinit var seekBarChangeCaptor: ArgumentCaptor<SeekBar.OnSeekBarChangeListener>
@Mock
private lateinit var seekBar: SeekBar
+ private val uiEventLogger = UiEventLoggerFake()
private var mFalsingManager: FalsingManagerFake = FalsingManagerFake()
private lateinit var mController: BrightnessSliderController
@@ -75,7 +78,8 @@
whenever(mirrorController.toggleSlider).thenReturn(mirror)
whenever(motionEvent.copy()).thenReturn(motionEvent)
- mController = BrightnessSliderController(brightnessSliderView, mFalsingManager)
+ mController =
+ BrightnessSliderController(brightnessSliderView, mFalsingManager, uiEventLogger)
mController.init()
mController.setOnChangedListener(listener)
}
@@ -190,6 +194,7 @@
@Test
fun testSeekBarTrackingStarted() {
whenever(brightnessSliderView.value).thenReturn(42)
+ val event = BrightnessSliderEvent.SLIDER_STARTED_TRACKING_TOUCH
mController.onViewAttached()
mController.setMirrorControllerAndMirror(mirrorController)
@@ -200,11 +205,14 @@
verify(listener).onChanged(eq(true), eq(42), eq(false))
verify(mirrorController).showMirror()
verify(mirrorController).setLocationAndSize(brightnessSliderView)
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ assertThat(uiEventLogger.eventId(0)).isEqualTo(event.id)
}
@Test
fun testSeekBarTrackingStopped() {
whenever(brightnessSliderView.value).thenReturn(23)
+ val event = BrightnessSliderEvent.SLIDER_STOPPED_TRACKING_TOUCH
mController.onViewAttached()
mController.setMirrorControllerAndMirror(mirrorController)
@@ -214,5 +222,7 @@
verify(listener).onChanged(eq(false), eq(23), eq(true))
verify(mirrorController).hideMirror()
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ assertThat(uiEventLogger.eventId(0)).isEqualTo(event.id)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ecaf137..48665fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -340,9 +340,9 @@
@Test
fun knownPluginAttached_clockAndListChanged_notLoaded() {
val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.falcon.one")
+ whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.clocks.metro")
val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.falcon.two")
+ whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.clocks.bignum")
var changeCallCount = 0
var listChangeCallCount = 0
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
index 9393a4f..ee3d870 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
@@ -60,18 +60,4 @@
assertThat(mController.canShowRotationButton()).isTrue()
}
-
- @Test
- fun ifTaskbarVisible_showRotationSuggestion() {
- mController.onNavigationBarWindowVisibilityChange( /* showing = */ false)
- mController.onBehaviorChanged(Display.DEFAULT_DISPLAY,
- WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
- mController.onNavigationModeChanged(WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON)
- mController.onTaskbarStateChange( /* visible = */ false, /* stashed = */ false)
- assertThat(mController.canShowRotationButton()).isFalse()
-
- mController.onTaskbarStateChange( /* visible = */ true, /* stashed = */ false)
-
- assertThat(mController.canShowRotationButton()).isTrue()
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
index 38a8f414..4a94dc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
@@ -21,21 +21,11 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
-import com.android.systemui.statusbar.notification.collection.ListEntry
-import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
-import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.withArgCaptor
import org.junit.Assert
import org.junit.Before
import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -46,43 +36,13 @@
private val groupMembershipManager: GroupMembershipManager = mock()
private val featureFlags = FakeFeatureFlags()
- private val pipeline: NotifPipeline = mock()
- private lateinit var beforeRenderListListener: OnBeforeRenderListListener
-
- private val summary1 = notificationEntry("foo", 1)
- private val summary2 = notificationEntry("bar", 1)
- private val entries =
- listOf<ListEntry>(
- GroupEntryBuilder()
- .setSummary(summary1)
- .setChildren(
- listOf(
- notificationEntry("foo", 2),
- notificationEntry("foo", 3),
- notificationEntry("foo", 4)
- )
- )
- .build(),
- GroupEntryBuilder()
- .setSummary(summary2)
- .setChildren(
- listOf(
- notificationEntry("bar", 2),
- notificationEntry("bar", 3),
- notificationEntry("bar", 4)
- )
- )
- .build(),
- notificationEntry("baz", 1)
- )
-
- private fun notificationEntry(pkg: String, id: Int) =
- NotificationEntryBuilder().setPkg(pkg).setId(id).build().apply { row = mock() }
+ private val entry1 = NotificationEntryBuilder().build()
+ private val entry2 = NotificationEntryBuilder().build()
@Before
fun setUp() {
- whenever(groupMembershipManager.getGroupSummary(summary1)).thenReturn(summary1)
- whenever(groupMembershipManager.getGroupSummary(summary2)).thenReturn(summary2)
+ whenever(groupMembershipManager.getGroupSummary(entry1)).thenReturn(entry1)
+ whenever(groupMembershipManager.getGroupSummary(entry2)).thenReturn(entry2)
gem = GroupExpansionManagerImpl(dumpManager, groupMembershipManager, featureFlags)
}
@@ -94,15 +54,15 @@
var listenerCalledCount = 0
gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
- gem.setGroupExpanded(summary1, false)
+ gem.setGroupExpanded(entry1, false)
Assert.assertEquals(0, listenerCalledCount)
- gem.setGroupExpanded(summary1, true)
+ gem.setGroupExpanded(entry1, true)
Assert.assertEquals(1, listenerCalledCount)
- gem.setGroupExpanded(summary2, true)
+ gem.setGroupExpanded(entry2, true)
Assert.assertEquals(2, listenerCalledCount)
- gem.setGroupExpanded(summary1, true)
+ gem.setGroupExpanded(entry1, true)
Assert.assertEquals(2, listenerCalledCount)
- gem.setGroupExpanded(summary2, false)
+ gem.setGroupExpanded(entry2, false)
Assert.assertEquals(3, listenerCalledCount)
}
@@ -113,39 +73,15 @@
var listenerCalledCount = 0
gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
- gem.setGroupExpanded(summary1, false)
+ gem.setGroupExpanded(entry1, false)
Assert.assertEquals(1, listenerCalledCount)
- gem.setGroupExpanded(summary1, true)
+ gem.setGroupExpanded(entry1, true)
Assert.assertEquals(2, listenerCalledCount)
- gem.setGroupExpanded(summary2, true)
+ gem.setGroupExpanded(entry2, true)
Assert.assertEquals(3, listenerCalledCount)
- gem.setGroupExpanded(summary1, true)
+ gem.setGroupExpanded(entry1, true)
Assert.assertEquals(4, listenerCalledCount)
- gem.setGroupExpanded(summary2, false)
+ gem.setGroupExpanded(entry2, false)
Assert.assertEquals(5, listenerCalledCount)
}
-
- @Test
- fun testSyncWithPipeline() {
- featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
- gem.attach(pipeline)
- beforeRenderListListener = withArgCaptor {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- val listener: OnGroupExpansionChangeListener = mock()
- gem.registerGroupExpansionChangeListener(listener)
-
- beforeRenderListListener.onBeforeRenderList(entries)
- verify(listener, never()).onGroupExpansionChange(any(), any())
-
- // Expand one of the groups.
- gem.setGroupExpanded(summary1, true)
- verify(listener).onGroupExpansionChange(summary1.row, true)
-
- // Empty the pipeline list and verify that the group is no longer expanded.
- beforeRenderListListener.onBeforeRenderList(emptyList())
- verify(listener).onGroupExpansionChange(summary1.row, false)
- verifyNoMoreInteractions(listener)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
index d5612e8..3f7fc97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
@@ -20,19 +20,21 @@
import android.testing.TestableLooper.RunWithLooper
import android.util.AttributeSet
import android.view.View
+import android.widget.Button
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertNotNull
-import junit.framework.Assert.assertNull
-import org.junit.Before
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
/** Tests for [NotifLayoutInflaterFactory] */
@SmallTest
@@ -40,87 +42,106 @@
@RunWithLooper
class NotifLayoutInflaterFactoryTest : SysuiTestCase() {
- @Mock private lateinit var attrs: AttributeSet
+ private lateinit var inflaterFactory: NotifLayoutInflaterFactory
- @Before
- fun before() {
- MockitoAnnotations.initMocks(this)
+ private val attrs: AttributeSet = mock()
+ private val row: ExpandableNotificationRow = mock()
+ private val textViewExpandedFactory =
+ createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_EXPANDED) { context, _ ->
+ Button(context)
+ }
+ private val textViewCollapsedFactory =
+ createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_CONTRACTED) { context, _ ->
+ Button(context)
+ }
+ private val textViewExpandedFactorySpy = spy(textViewExpandedFactory)
+ private val textViewCollapsedFactorySpy = spy(textViewCollapsedFactory)
+ private val viewFactorySpies = setOf(textViewExpandedFactorySpy, textViewCollapsedFactorySpy)
+
+ @Test
+ fun onCreateView_noMatchingViewForName_returnNull() {
+ // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts
+ val layoutType = FLAG_CONTENT_VIEW_EXPANDED
+ inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies)
+
+ // WHEN we try to inflate an ImageView for the expanded layout
+ val createdView = inflaterFactory.onCreateView("ImageView", context, attrs)
+
+ // THEN the inflater factory returns null
+ viewFactorySpies.forEach { viewFactory ->
+ verify(viewFactory).instantiate(row, layoutType, null, "ImageView", context, attrs)
+ }
+ assertThat(createdView).isNull()
}
@Test
- fun onCreateView_notMatchingViews_returnNull() {
- // GIVEN
- val layoutInflaterFactory =
- createNotifLayoutInflaterFactoryImpl(
- setOf(
- createReplacementViewFactory("TextView") { context, attrs ->
- FrameLayout(context)
- }
- )
- )
+ fun onCreateView_noMatchingViewForLayoutType_returnNull() {
+ // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts
+ val layoutType = FLAG_CONTENT_VIEW_HEADS_UP
+ inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies)
- // WHEN
- val createView = layoutInflaterFactory.onCreateView("ImageView", mContext, attrs)
+ // WHEN we try to inflate a TextView for the heads-up layout
+ val createdView = inflaterFactory.onCreateView("TextView", context, attrs)
- // THEN
- assertNull(createView)
+ // THEN the inflater factory returns null
+ viewFactorySpies.forEach { viewFactory ->
+ verify(viewFactory).instantiate(row, layoutType, null, "TextView", context, attrs)
+ }
+ assertThat(createdView).isNull()
}
@Test
fun onCreateView_matchingViews_returnReplacementView() {
- // GIVEN
- val layoutInflaterFactory =
- createNotifLayoutInflaterFactoryImpl(
- setOf(
- createReplacementViewFactory("TextView") { context, attrs ->
- FrameLayout(context)
- }
- )
- )
+ // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts
+ val layoutType = FLAG_CONTENT_VIEW_EXPANDED
+ inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies)
- // WHEN
- val createView = layoutInflaterFactory.onCreateView("TextView", mContext, attrs)
+ // WHEN we try to inflate a TextView for the expanded layout
+ val createdView = inflaterFactory.onCreateView("TextView", context, attrs)
- // THEN
- assertNotNull(createView)
- assertEquals(requireNotNull(createView)::class.java, FrameLayout::class.java)
+ // THEN the expanded viewFactory returns the replaced view
+ verify(textViewCollapsedFactorySpy)
+ .instantiate(row, layoutType, null, "TextView", context, attrs)
+ assertThat(createdView).isInstanceOf(Button::class.java)
}
@Test(expected = IllegalStateException::class)
fun onCreateView_multipleFactory_throwIllegalStateException() {
- // GIVEN
- val layoutInflaterFactory =
- createNotifLayoutInflaterFactoryImpl(
+ // GIVEN we have two factories that replaces TextViews in expanded layouts
+ val layoutType = FLAG_CONTENT_VIEW_EXPANDED
+ inflaterFactory =
+ NotifLayoutInflaterFactory(
+ row,
+ layoutType,
setOf(
- createReplacementViewFactory("TextView") { context, attrs ->
+ createReplacementViewFactory("TextView", layoutType) { context, _ ->
FrameLayout(context)
},
- createReplacementViewFactory("TextView") { context, attrs ->
+ createReplacementViewFactory("TextView", layoutType) { context, _ ->
LinearLayout(context)
}
)
)
- // WHEN
- layoutInflaterFactory.onCreateView("TextView", mContext, attrs)
+ // WHEN we try to inflate a TextView for the expanded layout
+ inflaterFactory.onCreateView("TextView", mContext, attrs)
}
- private fun createNotifLayoutInflaterFactoryImpl(
- replacementViewFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory>
- ) = NotifLayoutInflaterFactory(DumpManager(), replacementViewFactories)
-
private fun createReplacementViewFactory(
replacementName: String,
+ @InflationFlag replacementLayoutType: Int,
createView: (context: Context, attrs: AttributeSet) -> View
) =
object : NotifRemoteViewsFactory {
override fun instantiate(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int,
parent: View?,
name: String,
context: Context,
attrs: AttributeSet
): View? =
- if (replacementName == name) {
+ if (replacementName == name && replacementLayoutType == layoutType) {
createView(context, attrs)
} else {
null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index f55b0a8..ea87c80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -92,6 +92,7 @@
@Mock private ConversationNotificationProcessor mConversationNotificationProcessor;
@Mock private InflatedSmartReplyState mInflatedSmartReplyState;
@Mock private InflatedSmartReplyViewHolder mInflatedSmartReplies;
+ @Mock private NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
@Mock private NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
private final SmartReplyStateInflater mSmartReplyStateInflater =
@@ -124,6 +125,8 @@
TestableLooper.get(this));
ExpandableNotificationRow row = helper.createRow(mBuilder.build());
mRow = spy(row);
+ when(mNotifLayoutInflaterFactoryProvider.provide(any(), any()))
+ .thenReturn(mNotifLayoutInflaterFactory);
mNotificationInflater = new NotificationContentInflater(
mCache,
@@ -132,7 +135,7 @@
mock(MediaFeatureFlag.class),
mock(Executor.class),
mSmartReplyStateInflater,
- mNotifLayoutInflaterFactory);
+ mNotifLayoutInflaterFactoryProvider);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1ab2b38..3657bdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -175,7 +175,7 @@
mock(MediaFeatureFlag.class),
mock(Executor.class),
new MockSmartReplyInflater(),
- mock(NotifLayoutInflaterFactory.class));
+ mock(NotifLayoutInflaterFactory.Provider.class));
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
mock(NotifInflationErrorManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 0c28cbb..e249cec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -259,35 +259,6 @@
assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
}
- @Test
- fun userSwitchingInProgress_registersUserTrackerCallback() = runSelfCancelingTest {
- underTest = create(this)
-
- underTest.userSwitchingInProgress.launchIn(this)
- underTest.userSwitchingInProgress.launchIn(this)
- underTest.userSwitchingInProgress.launchIn(this)
-
- // Two callbacks registered - one for observing user switching and one for observing the
- // selected user
- assertThat(tracker.callbacks.size).isEqualTo(2)
- }
-
- @Test
- fun userSwitchingInProgress_propagatesStateFromUserTracker() = runSelfCancelingTest {
- underTest = create(this)
- assertThat(tracker.callbacks.size).isEqualTo(2)
-
- tracker.onUserChanging(0)
-
- var mostRecentSwitchingValue = false
- underTest.userSwitchingInProgress.onEach { mostRecentSwitchingValue = it }.launchIn(this)
-
- assertThat(mostRecentSwitchingValue).isTrue()
-
- tracker.onUserChanged(0)
- assertThat(mostRecentSwitchingValue).isFalse()
- }
-
private fun createUserInfo(
id: Int,
isGuest: Boolean,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
index 2ef1be7..51ce9f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
@@ -17,14 +17,24 @@
package com.android.systemui.biometrics.data.repository
-import kotlinx.coroutines.flow.Flow
+import com.android.systemui.biometrics.shared.model.LockoutMode
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
class FakeFacePropertyRepository : FacePropertyRepository {
private val faceSensorInfo = MutableStateFlow<FaceSensorInfo?>(null)
- override val sensorInfo: Flow<FaceSensorInfo?>
+ override val sensorInfo: StateFlow<FaceSensorInfo?>
get() = faceSensorInfo
+ private val lockoutModesForUser = mutableMapOf<Int, LockoutMode>()
+
+ fun setLockoutMode(userId: Int, mode: LockoutMode) {
+ lockoutModesForUser[userId] = mode
+ }
+ override suspend fun getLockoutMode(userId: Int): LockoutMode {
+ return lockoutModesForUser[userId]!!
+ }
+
fun setSensorInfo(value: FaceSensorInfo?) {
faceSensorInfo.value = value
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
index 10529e6..0847c85 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
@@ -21,7 +21,7 @@
override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
private val _panelExpansionAmount = MutableStateFlow(KeyguardBouncerConstants.EXPANSION_HIDDEN)
override val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
- private val _keyguardPosition = MutableStateFlow(0f)
+ private val _keyguardPosition = MutableStateFlow<Float?>(null)
override val keyguardPosition = _keyguardPosition.asStateFlow()
private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 51ee0c0..5ad19ee 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -56,10 +56,6 @@
)
override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
- private val _userSwitchingInProgress = MutableStateFlow(false)
- override val userSwitchingInProgress: Flow<Boolean>
- get() = _userSwitchingInProgress
-
override var mainUserId: Int = MAIN_USER_ID
override var lastSelectedNonGuestUserId: Int = mainUserId
@@ -120,8 +116,4 @@
fun setGuestUserAutoCreated(value: Boolean) {
_isGuestUserAutoCreated = value
}
-
- fun setUserSwitching(value: Boolean) {
- _userSwitchingInProgress.value = value
- }
}
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index a248d9e5..25f57b3 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -26,5 +26,6 @@
],
static_libs: [
"ukey2_jni",
+ "virtualdevice_flags_lib",
],
}
diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp
index 50a09b9..6526c78 100644
--- a/services/companion/java/com/android/server/companion/virtual/Android.bp
+++ b/services/companion/java/com/android/server/companion/virtual/Android.bp
@@ -9,4 +9,4 @@
srcs: [
"flags.aconfig",
],
-}
\ No newline at end of file
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index d1be5a9..f23fe2a 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -126,6 +126,7 @@
private final VirtualDeviceManagerService mService;
private final PendingTrampolineCallback mPendingTrampolineCallback;
private final int mOwnerUid;
+ private final VirtualDeviceLog mVirtualDeviceLog;
private final String mOwnerPackageName;
private int mDeviceId;
private @Nullable String mPersistentDeviceId;
@@ -197,6 +198,7 @@
Context context,
AssociationInfo associationInfo,
VirtualDeviceManagerService service,
+ VirtualDeviceLog virtualDeviceLog,
IBinder token,
AttributionSource attributionSource,
int deviceId,
@@ -210,6 +212,7 @@
context,
associationInfo,
service,
+ virtualDeviceLog,
token,
attributionSource,
deviceId,
@@ -228,6 +231,7 @@
Context context,
AssociationInfo associationInfo,
VirtualDeviceManagerService service,
+ VirtualDeviceLog virtualDeviceLog,
IBinder token,
AttributionSource attributionSource,
int deviceId,
@@ -240,6 +244,7 @@
VirtualDeviceParams params,
DisplayManagerGlobal displayManager) {
super(PermissionEnforcer.fromContext(context));
+ mVirtualDeviceLog = virtualDeviceLog;
mOwnerPackageName = attributionSource.getPackageName();
UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
mContext = context.createContextAsUser(ownerUserHandle, 0);
@@ -271,6 +276,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ mVirtualDeviceLog.logCreated(deviceId, mOwnerUid);
}
@VisibleForTesting
@@ -405,6 +411,7 @@
super.close_enforcePermission();
// Remove about-to-be-closed virtual device from the service before butchering it.
boolean removed = mService.removeVirtualDevice(mDeviceId);
+ mVirtualDeviceLog.logClosed(mDeviceId, mOwnerUid);
mDeviceId = Context.DEVICE_ID_INVALID;
// Device is already closed.
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
new file mode 100644
index 0000000..c65aa5b
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.companion.virtual;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayDeque;
+
+final class VirtualDeviceLog {
+ public static int TYPE_CREATED = 0x0;
+ public static int TYPE_CLOSED = 0x1;
+
+ private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(
+ "MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
+ private static final int MAX_ENTRIES = 16;
+
+ private final Context mContext;
+ private final ArrayDeque<LogEntry> mLogEntries = new ArrayDeque<>();
+
+ VirtualDeviceLog(Context context) {
+ mContext = context;
+ }
+
+ void logCreated(int deviceId, int ownerUid) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!Flags.dumpHistory()) {
+ return;
+ }
+ addEntry(new LogEntry(TYPE_CREATED, deviceId, System.currentTimeMillis(), ownerUid));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ void logClosed(int deviceId, int ownerUid) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!Flags.dumpHistory()) {
+ return;
+ }
+ addEntry(new LogEntry(TYPE_CLOSED, deviceId, System.currentTimeMillis(), ownerUid));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void addEntry(LogEntry entry) {
+ mLogEntries.push(entry);
+ if (mLogEntries.size() > MAX_ENTRIES) {
+ mLogEntries.removeLast();
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!Flags.dumpHistory()) {
+ return;
+ }
+ pw.println("VirtualDevice Log:");
+ UidToPackageNameCache packageNameCache = new UidToPackageNameCache(
+ mContext.getPackageManager());
+ for (LogEntry logEntry : mLogEntries) {
+ logEntry.dump(pw, " ", packageNameCache);
+
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ static class LogEntry {
+ private final int mType;
+ private final int mDeviceId;
+ private final long mTimestamp;
+ private final int mOwnerUid;
+
+ LogEntry(int type, int deviceId, long timestamp, int ownerUid) {
+ this.mType = type;
+ this.mDeviceId = deviceId;
+ this.mTimestamp = timestamp;
+ this.mOwnerUid = ownerUid;
+ }
+
+ void dump(PrintWriter pw, String prefix, UidToPackageNameCache packageNameCache) {
+ StringBuilder sb = new StringBuilder(prefix);
+ sb.append(DATE_FORMAT.format(Instant.ofEpochMilli(mTimestamp)));
+ sb.append(" - ");
+ sb.append(mType == TYPE_CREATED ? "START" : "CLOSE");
+ sb.append(" Device ID: ");
+ sb.append(mDeviceId);
+ sb.append(" ");
+ sb.append(mOwnerUid);
+ sb.append(" (");
+ sb.append(packageNameCache.getPackageName(mOwnerUid));
+ sb.append(")");
+ pw.println(sb);
+ }
+ }
+
+ private static class UidToPackageNameCache {
+ private final PackageManager mPackageManager;
+ private final SparseArray<String> mUidToPackagesCache = new SparseArray<>();
+
+ public UidToPackageNameCache(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ String getPackageName(int ownerUid) {
+ String[] packages;
+ if (mUidToPackagesCache.contains(ownerUid)) {
+ return mUidToPackagesCache.get(ownerUid);
+ } else {
+ packages = mPackageManager.getPackagesForUid(ownerUid);
+ String packageName = "";
+ if (packages != null && packages.length > 0) {
+ packageName = packages[0];
+ if (packages.length > 1) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(packageName)
+ .append(",...");
+ packageName = sb.toString();
+ }
+ }
+ mUidToPackagesCache.put(ownerUid, packageName);
+ return packageName;
+ }
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index e558498..7429fbe 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -85,6 +85,7 @@
private final Object mVirtualDeviceManagerLock = new Object();
private final VirtualDeviceManagerImpl mImpl;
private final VirtualDeviceManagerInternal mLocalService;
+ private VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext());
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
@@ -344,9 +345,11 @@
final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo,
- VirtualDeviceManagerService.this, token, attributionSource, deviceId,
+ VirtualDeviceManagerService.this, mVirtualDeviceLog, token, attributionSource,
+ deviceId,
cameraAccessController, mPendingTrampolineCallback, activityListener,
soundEffectListener, runningAppsChangedCallback, params);
+
synchronized (mVirtualDeviceManagerLock) {
if (mVirtualDevices.size() == 0) {
final long callindId = Binder.clearCallingIdentity();
@@ -521,6 +524,8 @@
for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
virtualDevicesSnapshot.get(i).dump(fd, fout, args);
}
+
+ mVirtualDeviceLog.dump(fout);
}
}
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 173a75b..7907d61 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -80,9 +80,9 @@
import android.util.apk.ApkSigningBlockUtils;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.expresslog.Histogram;
import com.android.internal.os.IBinaryTransparencyService;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.modules.expresslog.Histogram;
import com.android.server.pm.ApexManager;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.AndroidPackageSplit;
@@ -1391,7 +1391,7 @@
// Check the flag to determine whether biometric property verification is enabled. It's
// disabled by default.
if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BIOMETRICS,
- KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, false)) {
+ KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, true)) {
if (DEBUG) {
Slog.d(TAG, "Do not collect/verify biometric properties. Feature disabled by "
+ "DeviceConfig");
diff --git a/services/core/java/com/android/server/CertBlacklister.java b/services/core/java/com/android/server/CertBlacklister.java
index c16378b..e726c6a 100644
--- a/services/core/java/com/android/server/CertBlacklister.java
+++ b/services/core/java/com/android/server/CertBlacklister.java
@@ -31,17 +31,17 @@
import libcore.io.IoUtils;
/**
- * <p>CertBlacklister provides a simple mechanism for updating the platform blacklists for SSL
+ * <p>CertBlacklister provides a simple mechanism for updating the platform denylists for SSL
* certificate public keys and serial numbers.
*/
public class CertBlacklister extends Binder {
private static final String TAG = "CertBlacklister";
- private static final String BLACKLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
+ private static final String DENYLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
- public static final String PUBKEY_PATH = BLACKLIST_ROOT + "pubkey_blacklist.txt";
- public static final String SERIAL_PATH = BLACKLIST_ROOT + "serial_blacklist.txt";
+ public static final String PUBKEY_PATH = DENYLIST_ROOT + "pubkey_blacklist.txt";
+ public static final String SERIAL_PATH = DENYLIST_ROOT + "serial_blacklist.txt";
public static final String PUBKEY_BLACKLIST_KEY = "pubkey_blacklist";
public static final String SERIAL_BLACKLIST_KEY = "serial_blacklist";
@@ -66,14 +66,14 @@
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
- writeBlacklist();
+ writeDenylist();
}
public String getValue() {
return Settings.Secure.getString(mContentResolver, mKey);
}
- private void writeBlacklist() {
+ private void writeDenylist() {
new Thread("BlacklistUpdater") {
public void run() {
synchronized(mTmpDir) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7fb9580..4d249ab 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -73,8 +73,8 @@
import android.content.pm.UserInfo;
import android.content.res.ObbInfo;
import android.database.ContentObserver;
-import android.media.MediaCodecList;
import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.BatteryManager;
@@ -219,6 +219,8 @@
@GuardedBy("mLock")
private final Set<Integer> mCeStoragePreparedUsers = new ArraySet<>();
+ private volatile long mInternalStorageSize = 0;
+
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -3519,6 +3521,15 @@
return authority;
}
+ @Override
+ public long getInternalStorageBlockDeviceSize() throws RemoteException {
+ if (mInternalStorageSize == 0) {
+ mInternalStorageSize = mVold.getStorageSize();
+ }
+
+ return mInternalStorageSize;
+ }
+
/**
* Enforces that the caller is the {@link ExternalStorageService}
*
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d3bfc9a..942d35a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2514,30 +2514,36 @@
StopUserCallback callback = wait ? new StopUserCallback(userId) : null;
Slogf.d(TAG, "Calling stopUser(%d, %b, %s)", userId, force, callback);
- int res = mInterface.stopUser(userId, force, callback);
- if (res != ActivityManager.USER_OP_SUCCESS) {
- String txt = "";
- switch (res) {
- case ActivityManager.USER_OP_IS_CURRENT:
- txt = " (Can't stop current user)";
- break;
- case ActivityManager.USER_OP_UNKNOWN_USER:
- txt = " (Unknown user " + userId + ")";
- break;
- case ActivityManager.USER_OP_ERROR_IS_SYSTEM:
- txt = " (System user cannot be stopped)";
- break;
- case ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP:
- txt = " (Can't stop user " + userId
- + " - one of its related users can't be stopped)";
- break;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "shell_runStopUser-" + userId + "-[stopUser]");
+ try {
+ int res = mInterface.stopUser(userId, force, callback);
+ if (res != ActivityManager.USER_OP_SUCCESS) {
+ String txt = "";
+ switch (res) {
+ case ActivityManager.USER_OP_IS_CURRENT:
+ txt = " (Can't stop current user)";
+ break;
+ case ActivityManager.USER_OP_UNKNOWN_USER:
+ txt = " (Unknown user " + userId + ")";
+ break;
+ case ActivityManager.USER_OP_ERROR_IS_SYSTEM:
+ txt = " (System user cannot be stopped)";
+ break;
+ case ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP:
+ txt = " (Can't stop user " + userId
+ + " - one of its related users can't be stopped)";
+ break;
+ }
+ getErrPrintWriter().println("Switch failed: " + res + txt);
+ return -1;
+ } else if (callback != null) {
+ callback.waitForFinish();
}
- getErrPrintWriter().println("Switch failed: " + res + txt);
- return -1;
- } else if (callback != null) {
- callback.waitForFinish();
+ return 0;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- return 0;
}
int runIsUserStopped(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d426c79..2770833 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -105,6 +105,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
@@ -921,13 +922,24 @@
int stopUser(final int userId, final boolean force, boolean allowDelayedLocking,
final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) {
- checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser");
- Preconditions.checkArgument(userId >= 0, "Invalid user id %d", userId);
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
- synchronized (mLock) {
- return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback,
- keyEvictedCallback);
+ t.traceBegin("UserController"
+ + (force ? "-force" : "")
+ + (allowDelayedLocking ? "-allowDelayedLocking" : "")
+ + (stopUserCallback != null ? "-withStopUserCallback" : "")
+ + "-" + userId + "-[stopUser]");
+ try {
+ checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser");
+ Preconditions.checkArgument(userId >= 0, "Invalid user id %d", userId);
+
+ enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
+ synchronized (mLock) {
+ return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback,
+ keyEvictedCallback);
+ }
+ } finally {
+ t.traceEnd();
}
}
@@ -944,10 +956,10 @@
if (isCurrentUserLU(userId)) {
return USER_OP_IS_CURRENT;
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
int[] usersToStop = getUsersToStopLU(userId);
// If one of related users is system or current, no related users should be stopped
- for (int i = 0; i < usersToStop.length; i++) {
- int relatedUserId = usersToStop[i];
+ for (int relatedUserId : usersToStop) {
if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) {
if (DEBUG_MU) {
Slogf.i(TAG, "stopUsersLocked cannot stop related user " + relatedUserId);
@@ -956,8 +968,10 @@
if (force) {
Slogf.i(TAG,
"Force stop user " + userId + ". Related users will not be stopped");
+ t.traceBegin("stopSingleUserLU-force-" + userId + "-[stopUser]");
stopSingleUserLU(userId, allowDelayedLocking, stopUserCallback,
keyEvictedCallback);
+ t.traceEnd();
return USER_OP_SUCCESS;
}
return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
@@ -965,9 +979,11 @@
}
if (DEBUG_MU) Slogf.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
for (int userIdToStop : usersToStop) {
+ t.traceBegin("stopSingleUserLU-" + userIdToStop + "-[stopUser]");
stopSingleUserLU(userIdToStop, allowDelayedLocking,
userIdToStop == userId ? stopUserCallback : null,
userIdToStop == userId ? keyEvictedCallback : null);
+ t.traceEnd();
}
return USER_OP_SUCCESS;
}
@@ -1047,14 +1063,24 @@
&& uss.state != UserState.STATE_SHUTDOWN) {
uss.setState(UserState.STATE_STOPPING);
UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal();
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("setUserState-STATE_STOPPING-" + userId + "-[stopUser]");
userManagerInternal.setUserState(userId, uss.state);
+ t.traceEnd();
+ t.traceBegin("unassignUserFromDisplayOnStop-" + userId + "-[stopUser]");
userManagerInternal.unassignUserFromDisplayOnStop(userId);
+ t.traceEnd();
updateStartedUserArrayLU();
final boolean allowDelayedLockingCopied = allowDelayedLocking;
Runnable finishUserStoppingAsync = () ->
- mHandler.post(() -> finishUserStopping(userId, uss, allowDelayedLockingCopied));
+ mHandler.post(() -> {
+ TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
+ t2.traceBegin("finishUserStopping-" + userId + "-[stopUser]");
+ finishUserStopping(userId, uss, allowDelayedLockingCopied);
+ t2.traceEnd();
+ });
if (mInjector.getUserManager().isPreCreated(userId)) {
finishUserStoppingAsync.run();
@@ -1075,12 +1101,18 @@
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ asyncTraceEnd("broadcast-ACTION_USER_STOPPING-" + userId + "-[stopUser]",
+ userId);
finishUserStoppingAsync.run();
}
};
+ TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
+ t2.traceBegin("clearBroadcastQueueForUser-" + userId + "-[stopUser]");
// Clear broadcast queue for the user to avoid delivering stale broadcasts
mInjector.clearBroadcastQueueForUser(userId);
+ t2.traceEnd();
+ asyncTraceBegin("broadcast-ACTION_USER_STOPPING-" + userId + "-[stopUser]", userId);
// Kick things off.
mInjector.broadcastIntent(stoppingIntent,
null, stoppingReceiver, 0, null, null,
@@ -1111,7 +1143,10 @@
}
uss.setState(UserState.STATE_SHUTDOWN);
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("setUserState-STATE_SHUTDOWN-" + userId + "-[stopUser]");
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ t.traceEnd();
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
@@ -1119,7 +1154,12 @@
mInjector.getSystemServiceManager().onUserStopping(userId);
Runnable finishUserStoppedAsync = () ->
- mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking));
+ mHandler.post(() -> {
+ TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
+ t2.traceBegin("finishUserStopped-" + userId + "-[stopUser]");
+ finishUserStopped(uss, allowDelayedLocking);
+ t2.traceEnd();
+ });
if (mInjector.getUserManager().isPreCreated(userId)) {
finishUserStoppedAsync.run();
return;
@@ -1132,9 +1172,11 @@
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ asyncTraceEnd("broadcast-ACTION_SHUTDOWN-" + userId + "-[stopUser]", userId);
finishUserStoppedAsync.run();
}
};
+ asyncTraceBegin("broadcast-ACTION_SHUTDOWN-" + userId + "-[stopUser]", userId);
mInjector.broadcastIntent(shutdownIntent,
null, shutdownReceiver, 0, null, null, null,
AppOpsManager.OP_NONE,
@@ -1185,6 +1227,7 @@
}
}
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
if (stopped) {
Slogf.i(TAG, "Removing user state from UserManager.mUserStates for user #" + userId
+ " as a result of user being stopped");
@@ -1193,20 +1236,33 @@
mInjector.activityManagerOnUserStopped(userId);
// Clean up all state and processes associated with the user.
// Kill all the processes for the user.
+ t.traceBegin("forceStopUser-" + userId + "-[stopUser]");
forceStopUser(userId, "finish user");
+ t.traceEnd();
}
for (final IStopUserCallback callback : stopCallbacks) {
try {
- if (stopped) callback.userStopped(userId);
- else callback.userStopAborted(userId);
+ if (stopped) {
+ t.traceBegin("stopCallbacks.userStopped-" + userId + "-[stopUser]");
+ callback.userStopped(userId);
+ t.traceEnd();
+ } else {
+ t.traceBegin("stopCallbacks.userStopAborted-" + userId + "-[stopUser]");
+ callback.userStopAborted(userId);
+ t.traceEnd();
+ }
} catch (RemoteException ignored) {
}
}
if (stopped) {
+ t.traceBegin("systemServiceManagerOnUserStopped-" + userId + "-[stopUser]");
mInjector.systemServiceManagerOnUserStopped(userId);
+ t.traceEnd();
+ t.traceBegin("taskSupervisorRemoveUser-" + userId + "-[stopUser]");
mInjector.taskSupervisorRemoveUser(userId);
+ t.traceEnd();
// Remove the user if it is ephemeral.
if (userInfo.isEphemeral() && !userInfo.preCreated) {
@@ -3361,6 +3417,16 @@
return DEFAULT_USER_SWITCH_TIMEOUT_MS;
}
+ private static void asyncTraceBegin(String msg, int cookie) {
+ Slogf.d(TAG, "%s - asyncTraceBegin(%d)", msg, cookie);
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg, cookie);
+ }
+
+ private static void asyncTraceEnd(String msg, int cookie) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg, cookie);
+ Slogf.d(TAG, "%s - asyncTraceEnd(%d)", msg, cookie);
+ }
+
/**
* Uptime when any user was being unlocked most recently. 0 if no users have been unlocked
* yet. To avoid lock contention (since it's used by OomAdjuster), it's volatile internally.
@@ -3475,12 +3541,16 @@
ordered = false;
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
// TODO b/64165549 Verify that mLock is not held before calling AMS methods
synchronized (mService) {
- return mService.broadcastIntentLocked(null, null, null, intent, resolvedType,
- resultTo, resultCode, resultData, resultExtras, requiredPermissions, null,
- null, appOp, bOptions, ordered, sticky, callingPid, callingUid,
- realCallingUid, realCallingPid, userId);
+ t.traceBegin("broadcastIntent-" + userId + "-" + intent.getAction());
+ final int result = mService.broadcastIntentLocked(null, null, null, intent,
+ resolvedType, resultTo, resultCode, resultData, resultExtras,
+ requiredPermissions, null, null, appOp, bOptions, ordered, sticky,
+ callingPid, callingUid, realCallingUid, realCallingPid, userId);
+ t.traceEnd();
+ return result;
}
}
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
index 247094f..ba43c8d 100644
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -221,7 +221,7 @@
}
final AdiDeviceState deviceState = new AdiDeviceState(deviceType,
internalDeviceType, fields[1]);
- deviceState.setHasHeadTracker(Integer.parseInt(fields[2]) == 1);
+ deviceState.setSAEnabled(Integer.parseInt(fields[2]) == 1);
deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1);
deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1);
deviceState.setAudioDeviceCategory(audioDeviceCategory);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 4a523af..d0d5cda 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -2273,7 +2273,7 @@
if (btSCoOn) {
// Use the SCO device known to BtHelper so that it matches exactly
// what has been communicated to audio policy manager. The device
- // returned by requestedCommunicationDevice() can be a dummy SCO device if legacy
+ // returned by requestedCommunicationDevice() can be a placeholder SCO device if legacy
// APIs are used to start SCO audio.
AudioDeviceAttributes device = mBtHelper.getHeadsetAudioDevice();
if (device != null) {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 97e5c6f..54b34de 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -26,6 +26,7 @@
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.UserHandle;
+import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -48,12 +49,14 @@
// Upload the data every 50 attempts (average number of daily authentications).
private static final int AUTHENTICATION_UPLOAD_INTERVAL = 50;
// The maximum number of eligible biometric enrollment notification can be sent.
- private static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 2;
+ @VisibleForTesting
+ static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 2;
@NonNull private final Context mContext;
private final float mThreshold;
private final int mModality;
+ private boolean mPersisterInitialized = false;
@NonNull private final Map<Integer, AuthenticationStats> mUserAuthenticationStatsMap;
@@ -85,9 +88,15 @@
}
private void initializeUserAuthenticationStatsMap() {
- mAuthenticationStatsPersister = new AuthenticationStatsPersister(mContext);
- for (AuthenticationStats stats : mAuthenticationStatsPersister.getAllFrrStats(mModality)) {
- mUserAuthenticationStatsMap.put(stats.getUserId(), stats);
+ try {
+ mAuthenticationStatsPersister = new AuthenticationStatsPersister(mContext);
+ for (AuthenticationStats stats :
+ mAuthenticationStatsPersister.getAllFrrStats(mModality)) {
+ mUserAuthenticationStatsMap.put(stats.getUserId(), stats);
+ }
+ mPersisterInitialized = true;
+ } catch (IllegalStateException e) {
+ Slog.w(TAG, "Failed to initialize AuthenticationStatsPersister.", e);
}
}
@@ -106,9 +115,15 @@
AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId);
+ if (authenticationStats.getEnrollmentNotifications() >= MAXIMUM_ENROLLMENT_NOTIFICATIONS) {
+ return;
+ }
+
authenticationStats.authenticate(authenticated);
- persistDataIfNeeded(userId);
+ if (mPersisterInitialized) {
+ persistDataIfNeeded(userId);
+ }
sendNotificationIfNeeded(userId);
}
@@ -166,11 +181,13 @@
}
private void onUserRemoved(final int userId) {
- if (mAuthenticationStatsPersister == null) {
+ if (!mPersisterInitialized) {
initializeUserAuthenticationStatsMap();
}
- mUserAuthenticationStatsMap.remove(userId);
- mAuthenticationStatsPersister.removeFrrStats(userId);
+ if (mPersisterInitialized) {
+ mUserAuthenticationStatsMap.remove(userId);
+ mAuthenticationStatsPersister.removeFrrStats(userId);
+ }
}
/**
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
index 21e93a8..74e1410 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
@@ -21,6 +21,7 @@
import android.content.SharedPreferences;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.Environment;
+import android.os.UserHandle;
import android.util.Slog;
import org.json.JSONException;
@@ -72,14 +73,16 @@
JSONObject frrStatsJson = new JSONObject(frrStats);
if (modality == BiometricsProtoEnums.MODALITY_FACE) {
authenticationStatsList.add(new AuthenticationStats(
- getIntValue(frrStatsJson, USER_ID, -1 /* defaultValue */),
+ getIntValue(frrStatsJson, USER_ID,
+ UserHandle.USER_NULL /* defaultValue */),
getIntValue(frrStatsJson, FACE_ATTEMPTS),
getIntValue(frrStatsJson, FACE_REJECTIONS),
getIntValue(frrStatsJson, ENROLLMENT_NOTIFICATIONS),
modality));
} else if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) {
authenticationStatsList.add(new AuthenticationStats(
- getIntValue(frrStatsJson, USER_ID, -1 /* defaultValue */),
+ getIntValue(frrStatsJson, USER_ID,
+ UserHandle.USER_NULL /* defaultValue */),
getIntValue(frrStatsJson, FINGERPRINT_ATTEMPTS),
getIntValue(frrStatsJson, FINGERPRINT_REJECTIONS),
getIntValue(frrStatsJson, ENROLLMENT_NOTIFICATIONS),
@@ -138,13 +141,11 @@
// If there's existing frr stats in the file, we want to update the stats for the given
// modality and keep the stats for other modalities.
- if (frrStatJson != null) {
- frrStatsSet.add(buildFrrStats(frrStatJson, totalAttempts, rejectedAttempts,
- enrollmentNotifications, modality));
- } else {
- frrStatsSet.add(buildFrrStats(userId, totalAttempts, rejectedAttempts,
- enrollmentNotifications, modality));
+ if (frrStatJson == null) {
+ frrStatJson = new JSONObject().put(USER_ID, userId);
}
+ frrStatsSet.add(buildFrrStats(frrStatJson, totalAttempts, rejectedAttempts,
+ enrollmentNotifications, modality));
mSharedPreferences.edit().putStringSet(KEY, frrStatsSet).apply();
@@ -177,29 +178,6 @@
}
}
- // Build string for new user and new authentication stats.
- private String buildFrrStats(int userId, int totalAttempts, int rejectedAttempts,
- int enrollmentNotifications, int modality)
- throws JSONException {
- if (modality == BiometricsProtoEnums.MODALITY_FACE) {
- return new JSONObject()
- .put(USER_ID, userId)
- .put(FACE_ATTEMPTS, totalAttempts)
- .put(FACE_REJECTIONS, rejectedAttempts)
- .put(ENROLLMENT_NOTIFICATIONS, enrollmentNotifications)
- .toString();
- } else if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) {
- return new JSONObject()
- .put(USER_ID, userId)
- .put(FINGERPRINT_ATTEMPTS, totalAttempts)
- .put(FINGERPRINT_REJECTIONS, rejectedAttempts)
- .put(ENROLLMENT_NOTIFICATIONS, enrollmentNotifications)
- .toString();
- } else {
- return "";
- }
- }
-
private String getValue(JSONObject jsonObject, String key) throws JSONException {
return jsonObject.has(key) ? jsonObject.getString(key) : "";
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 0aac7c2..e072eeb 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -783,7 +783,7 @@
// This can be noisy, therefore we will allowlist sync adapters installed
// before we started checking for account access because they already know
// the account (they run before) which is the genie is out of the bottle.
- whiteListExistingSyncAdaptersIfNeeded();
+ allowListExistingSyncAdaptersIfNeeded();
mLogger.log("Sync manager initialized: " + Build.FINGERPRINT);
}
@@ -829,7 +829,7 @@
}
}
- private void whiteListExistingSyncAdaptersIfNeeded() {
+ private void allowListExistingSyncAdaptersIfNeeded() {
if (!mSyncStorageEngine.shouldGrantSyncAdaptersAccountAccess()) {
return;
}
diff --git a/services/core/java/com/android/server/content/SyncManagerConstants.java b/services/core/java/com/android/server/content/SyncManagerConstants.java
index 2a5858c..409b469 100644
--- a/services/core/java/com/android/server/content/SyncManagerConstants.java
+++ b/services/core/java/com/android/server/content/SyncManagerConstants.java
@@ -52,11 +52,11 @@
private static final int DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION = 5;
private int mMaxRetriesWithAppStandbyExemption = DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION;
- private static final String KEY_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS =
+ private static final String KEY_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS =
"exemption_temp_whitelist_duration_in_seconds";
- private static final int DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS = 10 * 60;
+ private static final int DEF_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS = 10 * 60;
private int mKeyExemptionTempWhitelistDurationInSeconds
- = DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS;
+ = DEF_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS;
protected SyncManagerConstants(Context context) {
super(null);
@@ -105,8 +105,8 @@
DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION);
mKeyExemptionTempWhitelistDurationInSeconds = parser.getInt(
- KEY_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS,
- DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS);
+ KEY_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS,
+ DEF_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS);
}
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index d647757..80c3a27 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -119,6 +119,8 @@
// hysteresis threshold.
private final long mBrighteningLightDebounceConfig;
private final long mDarkeningLightDebounceConfig;
+ private final long mBrighteningLightDebounceConfigIdle;
+ private final long mDarkeningLightDebounceConfigIdle;
// If true immediately after the screen is turned on the controller will try to adjust the
// brightness based on the current sensor reads. If false, the controller will collect more data
@@ -253,6 +255,7 @@
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
HysteresisLevels ambientBrightnessThresholdsIdle,
@@ -265,7 +268,8 @@
interactiveModeBrightnessMapper,
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
- darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
+ darkeningLightDebounceConfig, brighteningLightDebounceConfigIdle,
+ darkeningLightDebounceConfigIdle, resetAmbientLuxAfterWarmUpConfig,
ambientBrightnessThresholds, screenBrightnessThresholds,
ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context,
brightnessModeController, brightnessThrottler, idleModeBrightnessMapper,
@@ -280,6 +284,7 @@
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
HysteresisLevels ambientBrightnessThresholdsIdle,
@@ -303,6 +308,8 @@
mCurrentLightSensorRate = -1;
mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
+ mBrighteningLightDebounceConfigIdle = brighteningLightDebounceConfigIdle;
+ mDarkeningLightDebounceConfigIdle = darkeningLightDebounceConfigIdle;
mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
mAmbientLightHorizonLong = ambientLightHorizonLong;
mAmbientLightHorizonShort = ambientLightHorizonShort;
@@ -366,9 +373,10 @@
}
/**
- * @return The current brightness recommendation calculated from the current conditions.
- * @param brightnessEvent Event object to populate with details about why the specific
- * brightness was chosen.
+ * @param brightnessEvent Holds details about how the brightness is calculated.
+ *
+ * @return The current automatic brightness recommended value. Populates brightnessEvent
+ * parameters with details about how the brightness was calculated.
*/
public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) {
if (brightnessEvent != null) {
@@ -560,6 +568,8 @@
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
+ pw.println(" mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
+ pw.println(" mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
pw.println(" mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
pw.println(" mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
@@ -820,7 +830,8 @@
}
earliestValidTime = mAmbientLightRingBuffer.getTime(i);
}
- return earliestValidTime + mBrighteningLightDebounceConfig;
+ return earliestValidTime + (isInIdleMode()
+ ? mBrighteningLightDebounceConfigIdle : mBrighteningLightDebounceConfig);
}
private long nextAmbientLightDarkeningTransition(long time) {
@@ -832,7 +843,8 @@
}
earliestValidTime = mAmbientLightRingBuffer.getTime(i);
}
- return earliestValidTime + mDarkeningLightDebounceConfig;
+ return earliestValidTime + (isInIdleMode()
+ ? mDarkeningLightDebounceConfigIdle : mDarkeningLightDebounceConfig);
}
private void updateAmbientLux() {
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 46cd496..0d6635d 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -40,6 +40,7 @@
import android.view.SurfaceControl.Transaction;
import android.window.ScreenCapture;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
@@ -138,8 +139,13 @@
public static final int MODE_FADE = 2;
public ColorFade(int displayId) {
+ this(displayId, LocalServices.getService(DisplayManagerInternal.class));
+ }
+
+ @VisibleForTesting
+ ColorFade(int displayId, DisplayManagerInternal displayManagerInternal) {
mDisplayId = displayId;
- mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+ mDisplayManagerInternal = displayManagerInternal;
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 01eceda..c0c60a4 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -184,6 +184,7 @@
* <defaultRefreshRateInHbmSunlight>75</defaultRefreshRateInHbmSunlight>
* <lowerBlockingZoneConfigs>
* <defaultRefreshRate>75</defaultRefreshRate>
+ * <refreshRateThermalThrottlingId>id_of_a_throttling_map</refreshRateThermalThrottlingId>
* <blockingZoneThreshold>
* <displayBrightnessPoint>
* <lux>50</lux>
@@ -252,13 +253,19 @@
* <quirk>canSetBrightnessViaHwc</quirk>
* </quirks>
*
- * <autoBrightness enable="true">
+ * <autoBrightness enabled="true">
* <brighteningLightDebounceMillis>
* 2000
* </brighteningLightDebounceMillis>
* <darkeningLightDebounceMillis>
- * 1000
+ * 4000
* </darkeningLightDebounceMillis>
+ * <brighteningLightDebounceIdleMillis>
+ * 2000
+ * </brighteningLightDebounceIdleMillis>
+ * <darkeningLightDebounceIdleMillis>
+ * 1000
+ * </darkeningLightDebounceIdleMillis>
* <displayBrightnessMapping>
* <displayBrightnessPoint>
* <lux>50</lux>
@@ -649,6 +656,14 @@
private long mAutoBrightnessDarkeningLightDebounce =
INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+ // Represents the auto-brightness brightening light debounce for idle screen brightness mode.
+ private long mAutoBrightnessBrighteningLightDebounceIdle =
+ INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+
+ // Represents the auto-brightness darkening light debounce for idle screen brightness mode.
+ private long mAutoBrightnessDarkeningLightDebounceIdle =
+ INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+
// This setting allows non-default displays to have autobrightness enabled.
private boolean mAutoBrightnessAvailable = false;
// This stores the raw value loaded from the config file - true if not written.
@@ -732,6 +747,12 @@
private float[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
private float[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
+ /**
+ * Thermal throttling maps for the low and high blocking zones.
+ */
+ private String mLowBlockingZoneThermalMapId = null;
+ private String mHighBlockingZoneThermalMapId = null;
+
private final HashMap<String, ThermalBrightnessThrottlingData>
mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
@@ -1440,6 +1461,20 @@
}
/**
+ * @return Auto brightness darkening light debounce for idle screen brightness mode
+ */
+ public long getAutoBrightnessDarkeningLightDebounceIdle() {
+ return mAutoBrightnessDarkeningLightDebounceIdle;
+ }
+
+ /**
+ * @return Auto brightness brightening light debounce for idle screen brightness mode
+ */
+ public long getAutoBrightnessBrighteningLightDebounceIdle() {
+ return mAutoBrightnessBrighteningLightDebounceIdle;
+ }
+
+ /**
* @return Auto brightness brightening ambient lux levels
*/
public float[] getAutoBrightnessBrighteningLevelsLux() {
@@ -1536,6 +1571,13 @@
}
/**
+ * @return The refresh rate thermal map for low blocking zone.
+ */
+ public SparseArray<SurfaceControl.RefreshRateRange> getLowBlockingZoneThermalMap() {
+ return getThermalRefreshRateThrottlingData(mLowBlockingZoneThermalMapId);
+ }
+
+ /**
* @return An array of high display brightness thresholds. This, in combination with high
* ambient brightness thresholds help define buckets in which the refresh rate switching is not
* allowed.
@@ -1558,6 +1600,13 @@
}
/**
+ * @return The refresh rate thermal map for high blocking zone.
+ */
+ public SparseArray<SurfaceControl.RefreshRateRange> getHighBlockingZoneThermalMap() {
+ return getThermalRefreshRateThrottlingData(mHighBlockingZoneThermalMapId);
+ }
+
+ /**
* @return A mapping from screen off brightness sensor readings to lux values. This estimates
* the ambient lux when the screen is off to determine the initial brightness
*/
@@ -1664,6 +1713,10 @@
+ mAutoBrightnessBrighteningLightDebounce
+ ", mAutoBrightnessDarkeningLightDebounce= "
+ mAutoBrightnessDarkeningLightDebounce
+ + ", mAutoBrightnessBrighteningLightDebounceIdle= "
+ + mAutoBrightnessBrighteningLightDebounceIdle
+ + ", mAutoBrightnessDarkeningLightDebounceIdle= "
+ + mAutoBrightnessDarkeningLightDebounceIdle
+ ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
+ ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+ ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
@@ -1677,6 +1730,8 @@
+ ", mDefaultRefreshRateInHbmHdr= " + mDefaultRefreshRateInHbmHdr
+ ", mDefaultRefreshRateInHbmSunlight= " + mDefaultRefreshRateInHbmSunlight
+ ", mRefreshRateThrottlingMap= " + mRefreshRateThrottlingMap
+ + ", mLowBlockingZoneThermalMapId= " + mLowBlockingZoneThermalMapId
+ + ", mHighBlockingZoneThermalMapId= " + mHighBlockingZoneThermalMapId
+ "\n"
+ "mLowDisplayBrightnessThresholds= "
+ Arrays.toString(mLowDisplayBrightnessThresholds)
@@ -2127,9 +2182,13 @@
}
/**
- * Loads the refresh rate configurations pertaining to the upper blocking zones.
+ * Loads the refresh rate configurations pertaining to the lower blocking zones.
*/
private void loadLowerRefreshRateBlockingZones(BlockingZoneConfig lowerBlockingZoneConfig) {
+ if (lowerBlockingZoneConfig != null) {
+ mLowBlockingZoneThermalMapId =
+ lowerBlockingZoneConfig.getRefreshRateThermalThrottlingId();
+ }
loadLowerBlockingZoneDefaultRefreshRate(lowerBlockingZoneConfig);
loadLowerBrightnessThresholds(lowerBlockingZoneConfig);
}
@@ -2138,6 +2197,10 @@
* Loads the refresh rate configurations pertaining to the upper blocking zones.
*/
private void loadHigherRefreshRateBlockingZones(BlockingZoneConfig upperBlockingZoneConfig) {
+ if (upperBlockingZoneConfig != null) {
+ mHighBlockingZoneThermalMapId =
+ upperBlockingZoneConfig.getRefreshRateThermalThrottlingId();
+ }
loadHigherBlockingZoneDefaultRefreshRate(upperBlockingZoneConfig);
loadHigherBrightnessThresholds(upperBlockingZoneConfig);
}
@@ -2277,6 +2340,9 @@
final AutoBrightness autoBrightness = config.getAutoBrightness();
loadAutoBrightnessBrighteningLightDebounce(autoBrightness);
loadAutoBrightnessDarkeningLightDebounce(autoBrightness);
+ // Idle must be called after interactive, since we fall back to it if needed.
+ loadAutoBrightnessBrighteningLightDebounceIdle(autoBrightness);
+ loadAutoBrightnessDarkeningLightDebounceIdle(autoBrightness);
loadAutoBrightnessDisplayBrightnessMapping(autoBrightness);
loadEnableAutoBrightness(autoBrightness);
}
@@ -2312,6 +2378,37 @@
}
/**
+ * Loads the auto-brightness brightening light debounce for idle mode. Internally, this takes
+ * care of loading the value from the display config, and if not present, falls back to
+ * whichever interactive value was chosen.
+ */
+ private void loadAutoBrightnessBrighteningLightDebounceIdle(
+ AutoBrightness autoBrightnessConfig) {
+ if (autoBrightnessConfig == null
+ || autoBrightnessConfig.getBrighteningLightDebounceIdleMillis() == null) {
+ mAutoBrightnessBrighteningLightDebounceIdle = mAutoBrightnessBrighteningLightDebounce;
+ } else {
+ mAutoBrightnessBrighteningLightDebounceIdle =
+ autoBrightnessConfig.getBrighteningLightDebounceIdleMillis().intValue();
+ }
+ }
+
+ /**
+ * Loads the auto-brightness darkening light debounce for idle mode. Internally, this takes
+ * care of loading the value from the display config, and if not present, falls back to
+ * whichever interactive value was chosen.
+ */
+ private void loadAutoBrightnessDarkeningLightDebounceIdle(AutoBrightness autoBrightnessConfig) {
+ if (autoBrightnessConfig == null
+ || autoBrightnessConfig.getDarkeningLightDebounceIdleMillis() == null) {
+ mAutoBrightnessDarkeningLightDebounceIdle = mAutoBrightnessDarkeningLightDebounce;
+ } else {
+ mAutoBrightnessDarkeningLightDebounceIdle =
+ autoBrightnessConfig.getDarkeningLightDebounceIdleMillis().intValue();
+ }
+ }
+
+ /**
* Loads the auto-brightness display brightness mappings. Internally, this takes care of
* loading the value from the display config, and if not present, falls back to config.xml.
*/
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dd22cfd..79b7343 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1232,6 +1232,10 @@
.getAutoBrightnessBrighteningLightDebounce();
long darkeningLightDebounce = mDisplayDeviceConfig
.getAutoBrightnessDarkeningLightDebounce();
+ long brighteningLightDebounceIdle = mDisplayDeviceConfig
+ .getAutoBrightnessBrighteningLightDebounceIdle();
+ long darkeningLightDebounceIdle = mDisplayDeviceConfig
+ .getAutoBrightnessDarkeningLightDebounceIdle();
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
@@ -1271,7 +1275,8 @@
mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
- darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+ darkeningLightDebounce, brighteningLightDebounceIdle,
+ darkeningLightDebounceIdle, autoBrightnessResetAmbientLuxAfterWarmUp,
ambientBrightnessThresholds, screenBrightnessThresholds,
ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper,
@@ -1906,6 +1911,7 @@
final float currentBrightness = mPowerState.getScreenBrightness();
final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
+
if (isValidBrightnessValue(animateValue)
&& (animateValue != currentBrightness
|| sdrAnimateValue != currentSdrBrightness)) {
@@ -3536,6 +3542,7 @@
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
@@ -3549,6 +3556,7 @@
interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin,
brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate,
brighteningLightDebounceConfig, darkeningLightDebounceConfig,
+ brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessRangeController,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 0f89a6e..6c2240b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -1043,6 +1043,10 @@
.getAutoBrightnessBrighteningLightDebounce();
long darkeningLightDebounce = mDisplayDeviceConfig
.getAutoBrightnessDarkeningLightDebounce();
+ long brighteningLightDebounceIdle = mDisplayDeviceConfig
+ .getAutoBrightnessBrighteningLightDebounceIdle();
+ long darkeningLightDebounceIdle = mDisplayDeviceConfig
+ .getAutoBrightnessDarkeningLightDebounceIdle();
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
@@ -1082,7 +1086,8 @@
mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
- darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+ darkeningLightDebounce, brighteningLightDebounceIdle,
+ darkeningLightDebounceIdle, autoBrightnessResetAmbientLuxAfterWarmUp,
ambientBrightnessThresholds, screenBrightnessThresholds,
ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper,
@@ -2886,6 +2891,7 @@
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
@@ -2899,6 +2905,7 @@
interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin,
brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate,
brighteningLightDebounceConfig, darkeningLightDebounceConfig,
+ brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessModeController,
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index f15f036..f08878f 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1540,6 +1540,21 @@
private final Injector mInjector;
private final Handler mHandler;
+ private final IThermalEventListener.Stub mThermalListener =
+ new IThermalEventListener.Stub() {
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ @Temperature.ThrottlingStatus int currentStatus = temp.getStatus();
+ synchronized (mLock) {
+ if (mThermalStatus != currentStatus) {
+ mThermalStatus = currentStatus;
+ }
+ onBrightnessChangedLocked();
+ }
+ }
+ };
+ private boolean mThermalRegistered;
+
// Enable light sensor only when mShouldObserveAmbientLowChange is true or
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
// changeable and low power mode off. After initialization, these states will
@@ -1548,9 +1563,17 @@
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
+ @Nullable
+ private SparseArray<RefreshRateRange> mLowZoneRefreshRateForThermals;
private int mRefreshRateInLowZone;
+
+ @Nullable
+ private SparseArray<RefreshRateRange> mHighZoneRefreshRateForThermals;
private int mRefreshRateInHighZone;
+ @GuardedBy("mLock")
+ private @Temperature.ThrottlingStatus int mThermalStatus = Temperature.THROTTLING_NONE;
+
BrightnessObserver(Context context, Handler handler, Injector injector) {
mContext = context;
mHandler = handler;
@@ -1649,6 +1672,8 @@
R.integer.config_defaultRefreshRateInZone)
: displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
}
+ mLowZoneRefreshRateForThermals = displayDeviceConfig == null ? null
+ : displayDeviceConfig.getLowBlockingZoneThermalMap();
mRefreshRateInLowZone = refreshRateInLowZone;
}
@@ -1668,6 +1693,8 @@
R.integer.config_fixedRefreshRateInHighZone)
: displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate();
}
+ mHighZoneRefreshRateForThermals = displayDeviceConfig == null ? null
+ : displayDeviceConfig.getHighBlockingZoneThermalMap();
mRefreshRateInHighZone = refreshRateInHighZone;
}
@@ -2117,6 +2144,15 @@
if (insideLowZone) {
refreshRateVote =
Vote.forPhysicalRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+ if (mLowZoneRefreshRateForThermals != null) {
+ RefreshRateRange range = SkinThermalStatusObserver
+ .findBestMatchingRefreshRateRange(mThermalStatus,
+ mLowZoneRefreshRateForThermals);
+ if (range != null) {
+ refreshRateVote =
+ Vote.forPhysicalRefreshRates(range.min, range.max);
+ }
+ }
refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
}
@@ -2126,6 +2162,15 @@
refreshRateVote =
Vote.forPhysicalRefreshRates(mRefreshRateInHighZone,
mRefreshRateInHighZone);
+ if (mHighZoneRefreshRateForThermals != null) {
+ RefreshRateRange range = SkinThermalStatusObserver
+ .findBestMatchingRefreshRateRange(mThermalStatus,
+ mHighZoneRefreshRateForThermals);
+ if (range != null) {
+ refreshRateVote =
+ Vote.forPhysicalRefreshRates(range.min, range.max);
+ }
+ }
refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
}
@@ -2184,13 +2229,25 @@
+ mRefreshRateChangeable);
}
+ boolean registerForThermals = false;
if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
&& isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
registerLightSensor();
-
+ registerForThermals = mLowZoneRefreshRateForThermals != null
+ || mHighZoneRefreshRateForThermals != null;
} else {
unregisterSensorListener();
}
+
+ if (registerForThermals && !mThermalRegistered) {
+ mThermalRegistered = mInjector.registerThermalServiceListener(mThermalListener);
+ } else if (!registerForThermals && mThermalRegistered) {
+ mInjector.unregisterThermalServiceListener(mThermalListener);
+ mThermalRegistered = false;
+ synchronized (mLock) {
+ mThermalStatus = Temperature.THROTTLING_NONE; // reset
+ }
+ }
}
private void registerLightSensor() {
@@ -2821,6 +2878,7 @@
boolean isDozeState(Display d);
boolean registerThermalServiceListener(IThermalEventListener listener);
+ void unregisterThermalServiceListener(IThermalEventListener listener);
boolean supportsFrameRateOverride();
@@ -2922,6 +2980,19 @@
}
@Override
+ public void unregisterThermalServiceListener(IThermalEventListener listener) {
+ IThermalService thermalService = getThermalService();
+ if (thermalService == null) {
+ Slog.w(TAG, "Could not unregister thermal status. Service not available");
+ }
+ try {
+ thermalService.unregisterThermalEventListener(listener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unregister thermal status listener", e);
+ }
+ }
+
+ @Override
public boolean supportsFrameRateOverride() {
return SurfaceFlingerProperties.enable_frame_rate_override().orElse(true);
}
diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
index 58e1550..b29cda8 100644
--- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
+++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
@@ -64,6 +64,20 @@
mHandler = handler;
}
+ @Nullable
+ public static SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange(
+ @Temperature.ThrottlingStatus int currentStatus,
+ SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
+ SurfaceControl.RefreshRateRange foundRange = null;
+ for (int status = currentStatus; status >= 0; status--) {
+ foundRange = throttlingMap.get(status);
+ if (foundRange != null) {
+ break;
+ }
+ }
+ return foundRange;
+ }
+
void observe() {
// if failed to register thermal service listener, don't register display listener
if (!mInjector.registerThermalServiceListener(this)) {
@@ -228,20 +242,6 @@
}
}
- @Nullable
- private SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange(
- @Temperature.ThrottlingStatus int currentStatus,
- SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
- SurfaceControl.RefreshRateRange foundRange = null;
- for (int status = currentStatus; status >= 0; status--) {
- foundRange = throttlingMap.get(status);
- if (foundRange != null) {
- break;
- }
- }
- return foundRange;
- }
-
private void fallbackReportThrottlingIfNeeded(int displayId,
@Temperature.ThrottlingStatus int currentStatus) {
Vote vote = null;
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index d238dae..2ede56d 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -67,7 +67,7 @@
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setInputWindowInfo(mInputSurface, mWindowHandle);
- t.setLayer(mInputSurface, Integer.MAX_VALUE);
+ t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR);
t.setPosition(mInputSurface, 0, 0);
t.setCrop(mInputSurface, null /* crop to parent surface */);
t.show(mInputSurface);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index b8e9d5d..62660c4 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -381,6 +381,17 @@
public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
+ // The following are layer numbers used for z-ordering the input overlay layers on the display.
+ // This is used for ordering layers inside {@code DisplayContent#getInputOverlayLayer()}.
+ //
+ // The layer where gesture monitors are added.
+ public static final int INPUT_OVERLAY_LAYER_GESTURE_MONITOR = 1;
+ // Place the handwriting layer above gesture monitors so that styluses cannot trigger
+ // system gestures (e.g. navigation bar, edge-back, etc) while there is an active
+ // handwriting session.
+ public static final int INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE = 2;
+
+
private final String mVelocityTrackerStrategy;
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 0c889c2..7726f40 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -27,16 +27,13 @@
import android.view.SurfaceControl;
import android.view.WindowManager;
+import com.android.server.input.InputManagerService;
+
final class HandwritingEventReceiverSurface {
public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName();
static final boolean DEBUG = HandwritingModeController.DEBUG;
- // Place the layer at the highest layer so stylus cannot trigger gesture monitors
- // (e.g. navigation bar, edge-back, etc) while handwriting is ongoing.
- // TODO(b/217538817): Specify the ordering in WM by usage.
- private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE;
-
private final InputWindowHandle mWindowHandle;
private final InputChannel mClientChannel;
private final SurfaceControl mInputSurface;
@@ -68,7 +65,7 @@
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setInputWindowInfo(mInputSurface, mWindowHandle);
- t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER);
+ t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE);
t.setPosition(mInputSurface, 0, 0);
t.setCrop(mInputSurface, null /* crop to parent surface */);
t.show(mInputSurface);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8e7baf2..1ec8b10 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3275,15 +3275,18 @@
} else {
// If subtype is null, try to find the most applicable one from
// getCurrentInputMethodSubtype.
+ subtypeId = NOT_A_SUBTYPE_ID;
newSubtype = getCurrentInputMethodSubtypeLocked();
+ if (newSubtype != null) {
+ for (int i = 0; i < subtypeCount; ++i) {
+ if (Objects.equals(newSubtype, info.getSubtypeAt(i))) {
+ subtypeId = i;
+ break;
+ }
+ }
+ }
}
- if (newSubtype == null || oldSubtype == null) {
- Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
- + ", new subtype = " + newSubtype);
- notifyInputMethodSubtypeChangedLocked(userId, info, null);
- return;
- }
- if (!newSubtype.equals(oldSubtype)) {
+ if (!Objects.equals(newSubtype, oldSubtype)) {
setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
IInputMethodInvoker curMethod = getCurMethodLocked();
if (curMethod != null) {
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index ec03d9d..a7c986d 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -710,7 +710,7 @@
}
private String getCallingRulePusherPackageName(int callingUid) {
- // Obtain the system apps that are whitelisted in config_integrityRuleProviderPackages.
+ // Obtain the system apps that are allowlisted in config_integrityRuleProviderPackages.
List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps();
if (DEBUG_INTEGRITY_COMPONENT) {
Slog.i(
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d700c6a..8e9c21f 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -77,7 +77,6 @@
import java.util.Objects;
import java.util.Set;
-
/**
* A class that manages a user's synthetic password (SP) ({@link #SyntheticPassword}), along with a
* set of SP protectors that are independent ways that the SP is protected.
@@ -552,22 +551,48 @@
}
}
- private @Nullable IWeaver getWeaverServiceInternal() {
- // Try to get the AIDL service first
+ private @Nullable IWeaver getWeaverAidlService() {
+ final IWeaver aidlWeaver;
try {
- IWeaver aidlWeaver = IWeaver.Stub.asInterface(
- ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
- if (aidlWeaver != null) {
- Slog.i(TAG, "Using AIDL weaver service");
- try {
- aidlWeaver.asBinder().linkToDeath(new WeaverDiedRecipient(), 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to register Weaver death recipient", e);
- }
- return aidlWeaver;
- }
+ aidlWeaver =
+ IWeaver.Stub.asInterface(
+ ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
} catch (SecurityException e) {
Slog.w(TAG, "Does not have permissions to get AIDL weaver service");
+ return null;
+ }
+ if (aidlWeaver == null) {
+ return null;
+ }
+ final int aidlVersion;
+ try {
+ aidlVersion = aidlWeaver.getInterfaceVersion();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Cannot get AIDL weaver service version", e);
+ return null;
+ }
+ if (aidlVersion < 2) {
+ Slog.w(TAG,
+ "Ignoring AIDL weaver service v"
+ + aidlVersion
+ + " because only v2 and later are supported");
+ return null;
+ }
+ Slog.i(TAG, "Found AIDL weaver service v" + aidlVersion);
+ return aidlWeaver;
+ }
+
+ private @Nullable IWeaver getWeaverServiceInternal() {
+ // Try to get the AIDL service first
+ IWeaver aidlWeaver = getWeaverAidlService();
+ if (aidlWeaver != null) {
+ Slog.i(TAG, "Using AIDL weaver service");
+ try {
+ aidlWeaver.asBinder().linkToDeath(new WeaverDiedRecipient(), 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to register Weaver death recipient", e);
+ }
+ return aidlWeaver;
}
// If the AIDL service can't be found, look for the HIDL service
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 37bcfbb..5b87069 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -313,7 +313,7 @@
if (linkedItemLandingComponent != null) {
int callingUid = Binder.getCallingUid();
MediaServerUtils.enforcePackageName(
- linkedItemLandingComponent.getPackageName(), callingUid);
+ mContext, linkedItemLandingComponent.getPackageName(), callingUid);
if (!MediaServerUtils.isValidActivityComponentName(
mContext,
linkedItemLandingComponent,
diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java
index 60592fe..6a954d6 100644
--- a/services/core/java/com/android/server/media/MediaServerUtils.java
+++ b/services/core/java/com/android/server/media/MediaServerUtils.java
@@ -31,6 +31,7 @@
import com.android.server.LocalServices;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.List;
/** Util class for media server. */
@@ -63,7 +64,8 @@
* @throws IllegalArgumentException If the given {@code packageName} does not correspond to the
* given {@code uid}, and {@code uid} is not the root uid, or the shell uid.
*/
- public static void enforcePackageName(String packageName, int uid) {
+ public static void enforcePackageName(
+ @NonNull Context context, @NonNull String packageName, int uid) {
if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
return;
}
@@ -76,12 +78,16 @@
packageManagerInternal.getPackageUid(
packageName, 0 /* flags */, UserHandle.getUserId(uid));
if (!UserHandle.isSameApp(uid, actualUid)) {
+ String[] uidPackages = context.getPackageManager().getPackagesForUid(uid);
throw new IllegalArgumentException(
"packageName does not belong to the calling uid; "
+ "pkg="
+ packageName
+ ", uid="
- + uid);
+ + uid
+ + " ("
+ + Arrays.toString(uidPackages)
+ + ")");
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index f2242bf..f4c9518 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -542,7 +542,7 @@
int callingPid, int callingUid, String callingPackage, String reason) {
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(callingPackage, callingUid);
+ MediaServerUtils.enforcePackageName(mContext, callingPackage, callingUid);
if (targetUid != callingUid) {
boolean canAllowWhileInUse = mActivityManagerLocal
.canAllowWhileInUsePermissionInFgs(callingPid, callingUid, callingPackage);
@@ -1187,7 +1187,7 @@
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
if (cb == null) {
throw new IllegalArgumentException("Controller callback cannot be null");
@@ -1239,7 +1239,7 @@
final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
enforceMediaPermissions(packageName, pid, uid, userId);
MediaSessionRecordImpl record;
@@ -1270,7 +1270,7 @@
final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
enforceMediaPermissions(packageName, pid, uid, userId);
MediaSessionRecordImpl record;
@@ -1596,7 +1596,7 @@
final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
enforceMediaPermissions(packageName, pid, uid, userId);
synchronized (mLock) {
@@ -2110,7 +2110,7 @@
// If they gave us a component name verify they own the
// package
packageName = componentName.getPackageName();
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
}
// Check that they can make calls on behalf of the user and get the final user id
int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 009cc3b..6f0a4b4 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1928,6 +1928,7 @@
mConditionProviders.onUserRemoved(userId);
mAssistants.onUserRemoved(userId);
mHistoryManager.onUserRemoved(userId);
+ mPreferencesHelper.syncChannelsBypassingDnd();
handleSavePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
@@ -2379,6 +2380,7 @@
});
mPermissionHelper = permissionHelper;
mNotificationChannelLogger = channelLogger;
+ mUserProfiles.updateCache(getContext());
mPreferencesHelper = new PreferencesHelper(getContext(),
mPackageManagerClient,
mRankingHandler,
@@ -2387,6 +2389,7 @@
mPermissionManager,
mNotificationChannelLogger,
mAppOps,
+ mUserProfiles,
new SysUiStatsEvent.BuilderFactory(),
mShowReviewPermissionsNotification);
mRankingHelper = new RankingHelper(getContext(),
@@ -2442,8 +2445,6 @@
mZenModeHelper.initZenMode();
mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
- mUserProfiles.updateCache(getContext());
-
if (mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
telephonyManager.listen(new PhoneStateListener() {
@Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3f799dc..0e37f10 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -38,7 +38,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -53,7 +52,6 @@
import android.content.pm.UserInfo;
import android.metrics.LogMaker;
import android.net.Uri;
-import android.os.Binder;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
@@ -114,7 +112,6 @@
private static final int XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION = 4;
@VisibleForTesting
static final int UNKNOWN_UID = UserHandle.USER_NULL;
- private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
@VisibleForTesting
static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
@@ -196,6 +193,7 @@
private final PermissionManager mPermissionManager;
private final NotificationChannelLogger mNotificationChannelLogger;
private final AppOpsManager mAppOps;
+ private final ManagedServices.UserProfiles mUserProfiles;
private SparseBooleanArray mBadgingEnabled;
private SparseBooleanArray mBubblesEnabled;
@@ -204,14 +202,12 @@
private boolean mIsMediaNotificationFilteringEnabled = DEFAULT_MEDIA_NOTIFICATION_FILTERING;
private boolean mCurrentUserHasChannelsBypassingDnd;
private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
- private boolean mShowReviewPermissionsNotification;
-
- private boolean mAllowInvalidShortcuts = false;
+ private final boolean mShowReviewPermissionsNotification;
public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager,
NotificationChannelLogger notificationChannelLogger,
- AppOpsManager appOpsManager,
+ AppOpsManager appOpsManager, ManagedServices.UserProfiles userProfiles,
SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
boolean showReviewPermissionsNotification) {
mContext = context;
@@ -222,6 +218,7 @@
mPm = pm;
mNotificationChannelLogger = notificationChannelLogger;
mAppOps = appOpsManager;
+ mUserProfiles = userProfiles;
mStatsEventBuilderFactory = statsEventBuilderFactory;
mShowReviewPermissionsNotification = showReviewPermissionsNotification;
@@ -435,7 +432,7 @@
channel.getConversationId() != null &&
channel.getConversationId().contains(
PLACEHOLDER_CONVERSATION_ID);
- return mAllowInvalidShortcuts || (!mAllowInvalidShortcuts && !isInvalidShortcutChannel);
+ return !isInvalidShortcutChannel;
}
private boolean isDeletionOk(NotificationChannel nc) {
@@ -1790,8 +1787,9 @@
* Syncs {@link #mCurrentUserHasChannelsBypassingDnd} with the current user's notification
* policy before updating. Must be called:
* <ul>
- * <li>On system init, after channels and DND configurations are loaded.</li>
- * <li>When the current user changes, after the corresponding DND config is loaded.</li>
+ * <li>On system init, after channels and DND configurations are loaded.
+ * <li>When the current user is switched, after the corresponding DND config is loaded.
+ * <li>If users are removed (the removed user could've been a profile of the current one).
* </ul>
*/
void syncChannelsBypassingDnd() {
@@ -1805,20 +1803,19 @@
/**
* Updates the user's NotificationPolicy based on whether the current userId has channels
* bypassing DND. It should be called whenever a channel is created, updated, or deleted, or
- * when the current user is switched.
+ * when the current user (or its profiles) change.
*/
private void updateCurrentUserHasChannelsBypassingDnd(int callingUid,
boolean fromSystemOrSystemUi) {
ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>();
- final int currentUserId = getCurrentUser();
+ final IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
synchronized (mPackagePreferences) {
final int numPackagePreferences = mPackagePreferences.size();
for (int i = 0; i < numPackagePreferences; i++) {
final PackagePreferences r = mPackagePreferences.valueAt(i);
- // Package isn't associated with the current userId
- if (currentUserId != UserHandle.getUserId(r.uid)) {
- continue;
+ if (!currentUserIds.contains(UserHandle.getUserId(r.uid))) {
+ continue; // Package isn't associated with any profile of the current userId.
}
for (NotificationChannel channel : r.channels.values()) {
@@ -1842,13 +1839,6 @@
}
}
- private int getCurrentUser() {
- final long identity = Binder.clearCallingIdentity();
- int currentUserId = ActivityManager.getCurrentUser();
- Binder.restoreCallingIdentity(identity);
- return currentUserId;
- }
-
private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
// Channel is in a group that's blocked
if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b5ec136..66a1703 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -459,8 +459,8 @@
// Do not uninstall the APK if an app should be cached
boolean keepUninstalledPackage =
mPm.shouldKeepUninstalledPackageLPr(packageName);
- if (ps.isAnyInstalled(
- mUserManagerInternal.getUserIds()) || keepUninstalledPackage) {
+ if (ps.isInstalledOrHasDataOnAnyOtherUser(
+ mUserManagerInternal.getUserIds(), userId) || keepUninstalledPackage) {
// Other users still have this package installed, so all
// we need to do is clear this user's data and save that
// it is uninstalled.
@@ -555,6 +555,7 @@
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
+ ps.setCeDataInode(-1, nextUserId);
}
mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
@@ -615,7 +616,9 @@
Slog.d(TAG, "Marking package:" + ps.getPackageName()
+ " uninstalled for user:" + nextUserId);
}
- ps.setUserState(nextUserId, 0, COMPONENT_ENABLED_STATE_DEFAULT,
+ ps.setUserState(nextUserId,
+ ps.getCeDataInode(nextUserId),
+ COMPONENT_ENABLED_STATE_DEFAULT,
false /*installed*/,
true /*stopped*/,
true /*notLaunched*/,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c63d8be..f0bbd35 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5179,9 +5179,6 @@
public int getUserMinAspectRatio(@NonNull String packageName, int userId) {
final Computer snapshot = snapshotComputer();
final int callingUid = Binder.getCallingUid();
- snapshot.enforceCrossUserPermission(
- callingUid, userId, false /* requireFullPermission */,
- false /* checkShell */, "getUserMinAspectRatio");
final PackageStateInternal packageState = snapshot
.getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
return packageState == null ? USER_MIN_ASPECT_RATIO_UNSET
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 14693a6..7cac3e1 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -809,9 +809,16 @@
return changed;
}
- boolean isAnyInstalled(int[] users) {
- for (int user: users) {
- if (readUserState(user).isInstalled()) {
+ boolean isInstalledOrHasDataOnAnyOtherUser(int[] allUsers, int currentUser) {
+ for (int user: allUsers) {
+ if (user == currentUser) {
+ continue;
+ }
+ final PackageUserStateInternal userState = readUserState(user);
+ if (userState.isInstalled()) {
+ return true;
+ }
+ if (userState.getCeDataInode() > 0) {
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b6da462..307867c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5186,9 +5186,12 @@
pw.print(" instant=");
pw.print(userState.isInstantApp());
pw.print(" virtual=");
- pw.println(userState.isVirtualPreload());
+ pw.print(userState.isVirtualPreload());
pw.print(" quarantined=");
pw.print(userState.isQuarantined());
+
+ // Dump install state with additional indentation on their own lines.
+ pw.println();
pw.print(" installReason=");
pw.println(userState.getInstallReason());
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2f68021..b3aa09b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -29,6 +29,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.O;
+import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -338,6 +339,7 @@
// The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS
static final int SHORT_PRESS_PRIMARY_NOTHING = 0;
static final int SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS = 1;
+ static final int SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY = 2;
// Must match: config_longPressOnStemPrimaryBehavior in config.xml
// The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS
@@ -609,6 +611,9 @@
// What we do when the user double-taps on home
private int mDoubleTapOnHomeBehavior;
+ // Must match config_primaryShortPressTargetActivity in config.xml
+ ComponentName mPrimaryShortPressTargetActivity;
+
// Whether to lock the device after the next dreaming transition has finished.
private boolean mLockAfterDreamingTransitionFinished;
@@ -1374,7 +1379,7 @@
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,
"Power - Long Press - Go To Assistant");
- final int powerKeyDeviceId = Integer.MIN_VALUE;
+ final int powerKeyDeviceId = INVALID_INPUT_DEVICE_ID;
launchAssistAction(null, powerKeyDeviceId, eventTime,
AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
break;
@@ -1461,23 +1466,59 @@
}
private void stemPrimarySinglePressAction(int behavior) {
+ if (DEBUG_INPUT) {
+ Slog.d(TAG, "stemPrimarySinglePressAction: behavior=" + behavior);
+ }
+ if (behavior == SHORT_PRESS_PRIMARY_NOTHING) return;
+
+ final boolean keyguardActive = mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
+ if (keyguardActive) {
+ // If keyguarded then notify the keyguard.
+ mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
+ return;
+ }
switch (behavior) {
- case SHORT_PRESS_PRIMARY_NOTHING:
- break;
case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing stem primary short press action behavior.");
}
- final boolean keyguardActive =
- mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
- if (!keyguardActive) {
- Intent intent = new Intent(Intent.ACTION_ALL_APPS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
+ allAppsIntent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ startActivityAsUser(allAppsIntent, UserHandle.CURRENT_OR_SELF);
+ break;
+ case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
+ if (DEBUG_INPUT) {
+ Slog.d(
+ TAG,
+ "Executing stem primary short press action behavior for launching "
+ + "target activity.");
+ }
+ if (mPrimaryShortPressTargetActivity != null) {
+ Intent targetActivityIntent = new Intent();
+ targetActivityIntent.setComponent(mPrimaryShortPressTargetActivity);
+ ResolveInfo resolveInfo =
+ mContext.getPackageManager()
+ .resolveActivity(targetActivityIntent, /* flags= */ 0);
+ if (resolveInfo != null) {
+ targetActivityIntent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ startActivityAsUser(targetActivityIntent, UserHandle.CURRENT_OR_SELF);
+ } else {
+ Slog.wtf(
+ TAG,
+ "Could not resolve activity with : "
+ + mPrimaryShortPressTargetActivity.flattenToString()
+ + " name.");
+ }
} else {
- // If keyguarded then notify the keyguard.
- mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
+ Slog.wtf(
+ TAG,
+ "mPrimaryShortPressTargetActivity must not be null and correctly"
+ + " specified");
}
break;
}
@@ -1527,7 +1568,7 @@
}
}
- private void stemPrimaryLongPress() {
+ private void stemPrimaryLongPress(long eventTime) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing stem primary long press action behavior.");
}
@@ -1536,7 +1577,12 @@
case LONG_PRESS_PRIMARY_NOTHING:
break;
case LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT:
- launchVoiceAssist(/* allowDuringSetup= */false);
+ final int stemPrimaryKeyDeviceId = INVALID_INPUT_DEVICE_ID;
+ launchAssistAction(
+ null,
+ stemPrimaryKeyDeviceId,
+ eventTime,
+ AssistUtils.INVOCATION_TYPE_UNKNOWN);
break;
}
}
@@ -2159,6 +2205,7 @@
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mSensorPrivacyManager = mContext.getSystemService(SensorPrivacyManager.class);
+ mSearchManager = mContext.getSystemService(SearchManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -2295,6 +2342,9 @@
mPowerDoublePressTargetActivity = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_doublePressOnPowerTargetActivity));
+ mPrimaryShortPressTargetActivity = ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_primaryShortPressTargetActivity));
mShortPressOnSleepBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnSleepBehavior);
mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
@@ -2658,7 +2708,7 @@
@Override
void onLongPress(long eventTime) {
- stemPrimaryLongPress();
+ stemPrimaryLongPress(eventTime);
}
@Override
@@ -3905,7 +3955,7 @@
// Add Intent Extra data.
Bundle args = null;
args = new Bundle();
- if (deviceId > Integer.MIN_VALUE) {
+ if (deviceId != INVALID_INPUT_DEVICE_ID) {
args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, deviceId);
}
if (hint != null) {
@@ -3914,8 +3964,15 @@
args.putLong(Intent.EXTRA_TIME, eventTime);
args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType);
- ((SearchManager) mContext.createContextAsUser(UserHandle.of(mCurrentUserId), 0)
- .getSystemService(Context.SEARCH_SERVICE)).launchAssist(args);
+ if (mSearchManager != null) {
+ mSearchManager.launchAssist(args);
+ } else {
+ // Fallback to status bar if search manager doesn't exist (e.g. on wear).
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.startAssist(args);
+ }
+ }
}
/**
@@ -3926,39 +3983,16 @@
final boolean keyguardActive =
mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
if (!keyguardActive) {
- if (mHasFeatureWatch && isInRetailMode()) {
- launchRetailVoiceAssist(allowDuringSetup);
- } else {
- startVoiceAssistIntent(allowDuringSetup);
- }
+ startActivityAsUser(
+ new Intent(Intent.ACTION_VOICE_ASSIST),
+ /* bundle= */ null,
+ UserHandle.CURRENT_OR_SELF,
+ allowDuringSetup);
} else {
mKeyguardDelegate.dismissKeyguardToLaunch(new Intent(Intent.ACTION_VOICE_ASSIST));
}
}
- private void launchRetailVoiceAssist(boolean allowDuringSetup) {
- Intent retailIntent = new Intent(ACTION_VOICE_ASSIST_RETAIL);
- ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
- retailIntent, /* flags= */0);
- if (resolveInfo != null) {
- retailIntent.setComponent(
- new ComponentName(resolveInfo.activityInfo.packageName,
- resolveInfo.activityInfo.name));
- startActivityAsUser(retailIntent, null, UserHandle.CURRENT_OR_SELF,
- allowDuringSetup);
- } else {
- Slog.w(TAG, "Couldn't find an app to process " + ACTION_VOICE_ASSIST_RETAIL
- + ". Fall back to start " + Intent.ACTION_VOICE_ASSIST);
- startVoiceAssistIntent(allowDuringSetup);
- }
- }
-
- private void startVoiceAssistIntent(boolean allowDuringSetup) {
- Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
- startActivityAsUser(intent, null, UserHandle.CURRENT_OR_SELF,
- allowDuringSetup);
- }
-
private boolean isInRetailMode() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
@@ -3981,13 +4015,6 @@
}
}
- private SearchManager getSearchManager() {
- if (mSearchManager == null) {
- mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
- }
- return mSearchManager;
- }
-
private void preloadRecentApps() {
mPreloadedRecentApps = true;
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
@@ -6471,6 +6498,8 @@
return "SHORT_PRESS_PRIMARY_NOTHING";
case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
return "SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS";
+ case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
+ return "SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY";
default:
return Integer.toString(behavior);
}
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 33fc6fb..32a21c5 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -215,7 +215,7 @@
return true;
}
- // The package is now a part of the forced scoped storage whitelist
+ // The package is now a part of the forced scoped storage allowlist
if (isForcedScopedStorage) {
return true;
}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 577468b..33bed3d 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -56,7 +56,6 @@
public final class HintManagerService extends SystemService {
private static final String TAG = "HintManagerService";
private static final boolean DEBUG = false;
- private static final int MAX_HINT_SESSION_COUNT_PER_UID = 20;
@VisibleForTesting final long mHintSessionPreferredRate;
// Multi-level map storing all active AppHintSessions.
@@ -368,23 +367,6 @@
+ " not be empty.");
final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID) {
- int sessionCount = 0;
- synchronized (mLock) {
- ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
- mActiveSessions.get(callingUid);
- if (tokenMap != null) {
- for (ArraySet<AppHintSession> arr : tokenMap.values()) {
- sessionCount += arr.size();
- }
- }
- }
- if (sessionCount >= MAX_HINT_SESSION_COUNT_PER_UID) {
- throw new IllegalStateException(
- "Max session count limit reached: " + sessionCount);
- }
- }
-
final int callingTgid = Process.getThreadGroupLeader(Binder.getCallingPid());
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/security/KeyChainSystemService.java b/services/core/java/com/android/server/security/KeyChainSystemService.java
index da1ff73..caa5340 100644
--- a/services/core/java/com/android/server/security/KeyChainSystemService.java
+++ b/services/core/java/com/android/server/security/KeyChainSystemService.java
@@ -54,7 +54,7 @@
/**
* Maximum time limit for the KeyChain app to deal with packages being removed.
*/
- private static final int KEYCHAIN_IDLE_WHITELIST_DURATION_MS = 30 * 1000;
+ private static final int KEYCHAIN_IDLE_ALLOWLIST_DURATION_MS = 30 * 1000;
public KeyChainSystemService(final Context context) {
super(context);
@@ -105,7 +105,7 @@
final DeviceIdleInternal idleController =
LocalServices.getService(DeviceIdleInternal.class);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName,
- KEYCHAIN_IDLE_WHITELIST_DURATION_MS, user.getIdentifier(), false,
+ KEYCHAIN_IDLE_ALLOWLIST_DURATION_MS, user.getIdentifier(), false,
REASON_KEY_CHAIN, "keychain");
getContext().startServiceAsUser(intent, user);
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
index 2bd7383..1c5838c 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
@@ -105,14 +105,27 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
- new RemoteProvisioningShellCommand().dump(pw);
+ final int callerUid = Binder.getCallingUidOrThrow();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ new RemoteProvisioningShellCommand(getContext(), callerUid).dump(pw);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
@Override
public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
ParcelFileDescriptor err, String[] args) {
- return new RemoteProvisioningShellCommand().exec(this, in.getFileDescriptor(),
- out.getFileDescriptor(), err.getFileDescriptor(), args);
+ final int callerUid = Binder.getCallingUidOrThrow();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return new RemoteProvisioningShellCommand(getContext(), callerUid).exec(this,
+ in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+ args);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
index 187b939..4a6d746 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
@@ -16,22 +16,30 @@
package com.android.server.security.rkp;
+import android.content.Context;
import android.hardware.security.keymint.DeviceInfo;
import android.hardware.security.keymint.IRemotelyProvisionedComponent;
import android.hardware.security.keymint.MacedPublicKey;
import android.hardware.security.keymint.ProtectedData;
import android.hardware.security.keymint.RpcHardwareInfo;
+import android.os.CancellationSignal;
+import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ShellCommand;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
import android.util.IndentingPrintWriter;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.time.Duration;
import java.util.Base64;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
import co.nstant.in.cbor.CborDecoder;
import co.nstant.in.cbor.CborEncoder;
@@ -54,16 +62,17 @@
+ "csr [--challenge CHALLENGE] NAME\n"
+ " Generate and print a base64-encoded CSR from the named\n"
+ " IRemotelyProvisionedComponent. A base64-encoded challenge can be provided,\n"
- + " or else it defaults to an empty challenge.\n";
+ + " or else it defaults to an empty challenge.\n"
+ + "certify NAME\n"
+ + " Output the PEM-encoded certificate chain provisioned for the named\n"
+ + " IRemotelyProvisionedComponent.\n";
- @VisibleForTesting
static final String EEK_ED25519_BASE64 = "goRDoQEnoFgqpAEBAycgBiFYIJm57t1e5FL2hcZMYtw+YatXSH11N"
+ "ymtdoAy0rPLY1jZWEAeIghLpLekyNdOAw7+uK8UTKc7b6XN3Np5xitk/pk5r3bngPpmAIUNB5gqrJFcpyUUS"
+ "QY0dcqKJ3rZ41pJ6wIDhEOhASegWE6lAQECWCDQrsEVyirPc65rzMvRlh1l6LHd10oaN7lDOpfVmd+YCAM4G"
+ "CAEIVggvoXnRsSjQlpA2TY6phXQLFh+PdwzAjLS/F4ehyVfcmBYQJvPkOIuS6vRGLEOjl0gJ0uEWP78MpB+c"
+ "gWDvNeCvvpkeC1UEEvAMb9r6B414vAtzmwvT/L1T6XUg62WovGHWAQ=";
- @VisibleForTesting
static final String EEK_P256_BASE64 = "goRDoQEmoFhNpQECAyYgASFYIPcUituX9MxT79JkEcTjdR9mH6RxDGzP"
+ "+glGgHSHVPKtIlggXn9b9uzk9hnM/xM3/Q+hyJPbGAZ2xF3m12p3hsMtr49YQC+XjkL7vgctlUeFR5NAsB/U"
+ "m0ekxESp8qEHhxDHn8sR9L+f6Dvg5zRMFfx7w34zBfTRNDztAgRgehXgedOK/ySEQ6EBJqBYcaYBAgJYIDVz"
@@ -74,14 +83,20 @@
private static final int ERROR = -1;
private static final int SUCCESS = 0;
+ private static final Duration BIND_TIMEOUT = Duration.ofSeconds(10);
+ private static final int KEY_ID = 452436;
+
+ private final Context mContext;
+ private final int mCallerUid;
private final Injector mInjector;
- RemoteProvisioningShellCommand() {
- this(new Injector());
+ RemoteProvisioningShellCommand(Context context, int callerUid) {
+ this(context, callerUid, new Injector());
}
- @VisibleForTesting
- RemoteProvisioningShellCommand(Injector injector) {
+ RemoteProvisioningShellCommand(Context context, int callerUid, Injector injector) {
+ mContext = context;
+ mCallerUid = callerUid;
mInjector = injector;
}
@@ -102,6 +117,8 @@
return list();
case "csr":
return csr();
+ case "certify":
+ return certify();
default:
return handleDefaultCommands(cmd);
}
@@ -232,7 +249,45 @@
return new CborDecoder(bais).decodeNext();
}
- @VisibleForTesting
+ private int certify() throws Exception {
+ String name = getNextArgRequired();
+
+ Executor executor = mContext.getMainExecutor();
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ OutcomeFuture<RemotelyProvisionedKey> key = new OutcomeFuture<>();
+ mInjector.getRegistrationProxy(mContext, mCallerUid, name, executor)
+ .getKeyAsync(KEY_ID, cancellationSignal, executor, key);
+ byte[] encodedCertChain = key.join().getEncodedCertChain();
+ ByteArrayInputStream is = new ByteArrayInputStream(encodedCertChain);
+ PrintWriter pw = getOutPrintWriter();
+ for (Certificate cert : CertificateFactory.getInstance("X.509").generateCertificates(is)) {
+ String encoded = Base64.getEncoder().encodeToString(cert.getEncoded());
+ pw.println("-----BEGIN CERTIFICATE-----");
+ pw.println(encoded.replaceAll("(.{64})", "$1\n").stripTrailing());
+ pw.println("-----END CERTIFICATE-----");
+ }
+ return SUCCESS;
+ }
+
+ /** Treat an OutcomeReceiver as a future for use in synchronous code. */
+ private static class OutcomeFuture<T> implements OutcomeReceiver<T, Exception> {
+ private CompletableFuture<T> mFuture = new CompletableFuture<>();
+
+ @Override
+ public void onResult(T result) {
+ mFuture.complete(result);
+ }
+
+ @Override
+ public void onError(Exception e) {
+ mFuture.completeExceptionally(e);
+ }
+
+ public T join() {
+ return mFuture.join();
+ }
+ }
+
static class Injector {
String[] getIrpcNames() {
return ServiceManager.getDeclaredInstances(IRemotelyProvisionedComponent.DESCRIPTOR);
@@ -248,5 +303,14 @@
}
return binder;
}
+
+ RegistrationProxy getRegistrationProxy(
+ Context context, int callerUid, String name, Executor executor) {
+ String irpc = IRemotelyProvisionedComponent.DESCRIPTOR + "/" + name;
+ OutcomeFuture<RegistrationProxy> registration = new OutcomeFuture<>();
+ RegistrationProxy.createAsync(
+ context, callerUid, irpc, BIND_TIMEOUT, executor, registration);
+ return registration.join();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 64c7c6f..649ab8f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7258,8 +7258,10 @@
}
void showStartingWindow(boolean taskSwitch) {
- showStartingWindow(null /* prev */, false /* newTask */, taskSwitch,
- false /* startActivity */, null);
+ // Pass the activity which contains starting window already.
+ final ActivityRecord prev = task.getActivity(
+ a -> a != this && a.mStartingData != null && a.showToCurrentUser());
+ showStartingWindow(prev, false /* newTask */, taskSwitch, false /* startActivity */, null);
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5c82dba..59677f4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -327,6 +327,12 @@
*/
private SurfaceControl mOverlayLayer;
+ /**
+ * A SurfaceControl that contains input overlays used for cases where we need to receive input
+ * over the entire display.
+ */
+ private SurfaceControl mInputOverlayLayer;
+
/** A surfaceControl specifically for accessibility overlays. */
private SurfaceControl mA11yOverlayLayer;
@@ -1327,6 +1333,12 @@
transaction.reparent(mOverlayLayer, mSurfaceControl);
}
+ if (mInputOverlayLayer == null) {
+ mInputOverlayLayer = b.setName("Input Overlays").setParent(mSurfaceControl).build();
+ } else {
+ transaction.reparent(mInputOverlayLayer, mSurfaceControl);
+ }
+
if (mA11yOverlayLayer == null) {
mA11yOverlayLayer =
b.setName("Accessibility Overlays").setParent(mSurfaceControl).build();
@@ -1340,7 +1352,9 @@
.show(mSurfaceControl)
.setLayer(mOverlayLayer, Integer.MAX_VALUE)
.show(mOverlayLayer)
- .setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 1)
+ .setLayer(mInputOverlayLayer, Integer.MAX_VALUE - 1)
+ .show(mInputOverlayLayer)
+ .setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 2)
.show(mA11yOverlayLayer);
}
@@ -3351,6 +3365,7 @@
// -> this DisplayContent.
setRemoteInsetsController(null);
mOverlayLayer.release();
+ mInputOverlayLayer.release();
mA11yOverlayLayer.release();
mWindowingLayer.release();
mInputMonitor.onDisplayRemoved();
@@ -5704,6 +5719,10 @@
return mOverlayLayer;
}
+ SurfaceControl getInputOverlayLayer() {
+ return mInputOverlayLayer;
+ }
+
SurfaceControl getA11yOverlayLayer() {
return mA11yOverlayLayer;
}
@@ -7060,6 +7079,7 @@
new Transaction().reparent(sc, getSurfaceControl())
.reparent(mWindowingLayer, null)
.reparent(mOverlayLayer, null)
+ .reparent(mInputOverlayLayer, null)
.reparent(mA11yOverlayLayer, null)
.apply();
}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 73fdfe0..8cf4713 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -275,11 +275,17 @@
+ " - DisplayContent not found.");
return null;
}
+ final SurfaceControl inputOverlay = dc.getInputOverlayLayer();
+ if (inputOverlay == null) {
+ Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
+ + " - Input overlay layer is not initialized.");
+ return null;
+ }
return mService.makeSurfaceBuilder(dc.getSession())
.setContainerLayer()
.setName(name)
.setCallsite("createSurfaceForGestureMonitor")
- .setParent(dc.getSurfaceControl())
+ .setParent(inputOverlay)
.build();
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2f0c303..4089a0d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2277,7 +2277,7 @@
int finishTopCrashedActivities(WindowProcessController app, String reason) {
Task focusedRootTask = getTopDisplayFocusedRootTask();
final Task[] finishedTask = new Task[1];
- forAllTasks(rootTask -> {
+ forAllRootTasks(rootTask -> {
final Task t = rootTask.finishTopCrashedActivityLocked(app, reason);
if (rootTask == focusedRootTask || finishedTask[0] == null) {
finishedTask[0] = t;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 43430dd..f9bbc68 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -725,7 +725,7 @@
} catch (RemoteException e) {
}
}
- if (autoRemoveFromRecents(oldParent.asTaskFragment()) || isVoiceSession) {
+ if (shouldAutoRemoveFromRecents(oldParent.asTaskFragment()) || isVoiceSession) {
// Task creator asked to remove this when done, or this task was a voice
// interaction, so it should not remain on the recent tasks list.
mTaskSupervisor.mRecentTasks.remove(this);
@@ -1558,12 +1558,14 @@
return count > 0;
}
- private boolean autoRemoveFromRecents(TaskFragment oldParentFragment) {
+ private boolean shouldAutoRemoveFromRecents(TaskFragment oldParentFragment) {
// We will automatically remove the task either if it has explicitly asked for
// this, or it is empty and has never contained an activity that got shown to
- // the user, or it was being embedded in another Task.
- return autoRemoveRecents || (!hasChild() && !getHasBeenVisible()
- || (oldParentFragment != null && oldParentFragment.isEmbedded()));
+ // the user, or it was being embedded in another Task, or the display policy
+ // doesn't allow recents,
+ return autoRemoveRecents || (!hasChild() && !getHasBeenVisible())
+ || (oldParentFragment != null && oldParentFragment.isEmbedded())
+ || (mDisplayContent != null && !mDisplayContent.canShowTasksInHostDeviceRecents());
}
private void clearPinnedTaskIfNeed() {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 6ede345..3c85f08 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -1147,6 +1147,7 @@
Transition.asyncTraceBegin("animating", 0x41bfaf1 /* hashcode of TAG */);
} else if (!animatingState && mAnimatingState) {
t.setEarlyWakeupEnd();
+ mAtm.mWindowManager.requestTraversal();
mSnapshotController.setPause(false);
mAnimatingState = false;
Transition.asyncTraceEnd(0x41bfaf1 /* hashcode of TAG */);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 03efb1b..439b719 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8338,12 +8338,18 @@
+ displayId + " - DisplayContent not found.");
return null;
}
- //TODO (b/210039666): Use a method like add/removeDisplayOverlay if available.
+ final SurfaceControl inputOverlay = dc.getInputOverlayLayer();
+ if (inputOverlay == null) {
+ Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
+ + " - Input overlay layer is not initialized.");
+ return null;
+ }
+ // TODO(b/210039666): Use a method like add/removeDisplayOverlay if available.
return makeSurfaceBuilder(dc.getSession())
.setContainerLayer()
.setName("IME Handwriting Surface")
.setCallsite("getHandwritingSurfaceForDisplay")
- .setParent(dc.getSurfaceControl())
+ .setParent(inputOverlay)
.build();
}
}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 7104a80..d833fbd 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -459,16 +459,26 @@
<xs:complexType name="autoBrightness">
<xs:attribute name="enabled" type="xs:boolean" use="optional" default="true"/>
<xs:sequence>
- <!-- Sets the debounce for autoBrightness brightening in millis-->
+ <!-- Sets the debounce for autoBrightness brightening in millis -->
<xs:element name="brighteningLightDebounceMillis" type="xs:nonNegativeInteger"
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
- <!-- Sets the debounce for autoBrightness darkening in millis-->
+ <!-- Sets the debounce for autoBrightness darkening in millis -->
<xs:element name="darkeningLightDebounceMillis" type="xs:nonNegativeInteger"
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
+ <!-- Sets the debounce for autoBrightness brightening in millis while in idle mode -->
+ <xs:element name="brighteningLightDebounceIdleMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Sets the debounce for autoBrightness darkening in millis while in idle mode -->
+ <xs:element name="darkeningLightDebounceIdleMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
<!-- Sets the brightness mapping of the desired screen brightness in nits to the
corresponding lux for the current display -->
<xs:element name="displayBrightnessMapping" type="displayBrightnessMapping"
@@ -579,6 +589,10 @@
minOccurs="1" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
+ <xs:element type ="xs:string" name="refreshRateThermalThrottlingId">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
<xs:element name="blockingZoneThreshold" type="blockingZoneThreshold"
minOccurs="1" maxOccurs="1">
<xs:annotation name="final"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 507c9dc..d2ac1aae 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -3,11 +3,15 @@
public class AutoBrightness {
ctor public AutoBrightness();
+ method public final java.math.BigInteger getBrighteningLightDebounceIdleMillis();
method public final java.math.BigInteger getBrighteningLightDebounceMillis();
+ method public final java.math.BigInteger getDarkeningLightDebounceIdleMillis();
method public final java.math.BigInteger getDarkeningLightDebounceMillis();
method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
method public boolean getEnabled();
+ method public final void setBrighteningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
+ method public final void setDarkeningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
method public void setEnabled(boolean);
@@ -17,8 +21,10 @@
ctor public BlockingZoneConfig();
method public final com.android.server.display.config.BlockingZoneThreshold getBlockingZoneThreshold();
method public final java.math.BigInteger getDefaultRefreshRate();
+ method @Nullable public final String getRefreshRateThermalThrottlingId();
method public final void setBlockingZoneThreshold(com.android.server.display.config.BlockingZoneThreshold);
method public final void setDefaultRefreshRate(java.math.BigInteger);
+ method public final void setRefreshRateThermalThrottlingId(@Nullable String);
}
public class BlockingZoneThreshold {
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 9a19c2d..f0705ed 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -985,7 +985,7 @@
) {
val appOpPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as
AppIdAppOpPolicy
- val appOpName = AppOpsManager.permissionToOp(permissionName)
+ val appOpName = checkNotNull(AppOpsManager.permissionToOp(permissionName))
val mode = if (isGranted) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
}
diff --git a/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
index d7d2726..7858b30 100644
--- a/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
+++ b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
@@ -43,7 +43,7 @@
assertThat(packageInstallers).isNotEmpty()
packageInstallers.forEach {
- val exported = it.receivers.filter { it.exported }
+ val exported = it.receivers?.filter { it.exported }
assertWithMessage("Receivers should not be exported").that(exported).isEmpty()
}
}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
index b7a0cf3..e33ca77 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
@@ -138,14 +138,6 @@
}
@Test
- public void testGetUserMinAspectRatio_withCrossUserId() {
- final int crossUserId = UserHandle.myUserId() + 1;
- assertThrows(SecurityException.class,
- () -> mIPackageManager.getUserMinAspectRatio(
- mInstrumentation.getContext().getPackageName(), crossUserId));
- }
-
- @Test
public void testIsPackageSignedByKeySet_cannotDetectCrossUserPkg() throws Exception {
final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
assertThrows(IllegalArgumentException.class,
diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
index 2d93120..007c0db 100644
--- a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
+++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
@@ -21,12 +21,14 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.hardware.security.keymint.DeviceInfo;
import android.hardware.security.keymint.IRemotelyProvisionedComponent;
import android.hardware.security.keymint.MacedPublicKey;
@@ -34,28 +36,35 @@
import android.hardware.security.keymint.RpcHardwareInfo;
import android.os.Binder;
import android.os.FileUtils;
+import android.os.OutcomeReceiver;
+import android.os.Process;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
+import java.util.concurrent.Executor;
@RunWith(AndroidJUnit4.class)
public class RemoteProvisioningShellCommandTest {
+ private Context mContext;
+
private static class Injector extends RemoteProvisioningShellCommand.Injector {
- private final Map<String, IRemotelyProvisionedComponent> mIrpcs;
-
- Injector(Map irpcs) {
- mIrpcs = irpcs;
- }
+ Map<String, IRemotelyProvisionedComponent> mIrpcs;
+ Map<String, RegistrationProxy> mRegistrationProxies;
@Override
String[] getIrpcNames() {
@@ -70,6 +79,12 @@
}
return irpc;
}
+
+ @Override
+ RegistrationProxy getRegistrationProxy(
+ Context context, int callerUid, String name, Executor executor) {
+ return mRegistrationProxies.get(name);
+ }
}
private static class CommandResult {
@@ -111,10 +126,17 @@
code, FileUtils.readTextFile(out, 0, null), FileUtils.readTextFile(err, 0, null));
}
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ }
+
@Test
public void list_zeroInstances() throws Exception {
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of();
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of()));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"list"});
assertThat(res.getErr()).isEmpty();
assertThat(res.getCode()).isEqualTo(0);
@@ -124,8 +146,10 @@
@Test
public void list_oneInstances() throws Exception {
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", mock(IRemotelyProvisionedComponent.class));
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", mock(IRemotelyProvisionedComponent.class))));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"list"});
assertThat(res.getErr()).isEmpty();
assertThat(res.getCode()).isEqualTo(0);
@@ -134,10 +158,12 @@
@Test
public void list_twoInstances() throws Exception {
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of(
+ "default", mock(IRemotelyProvisionedComponent.class),
+ "strongbox", mock(IRemotelyProvisionedComponent.class));
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of(
- "default", mock(IRemotelyProvisionedComponent.class),
- "strongbox", mock(IRemotelyProvisionedComponent.class))));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"list"});
assertThat(res.getErr()).isEmpty();
assertThat(res.getCode()).isEqualTo(0);
@@ -158,8 +184,10 @@
}).when(defaultMock).generateCertificateRequest(
anyBoolean(), any(), any(), any(), any(), any());
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", defaultMock);
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", defaultMock)));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {
"csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
verify(defaultMock).generateCertificateRequest(
@@ -189,8 +217,10 @@
}).when(defaultMock).generateCertificateRequest(
anyBoolean(), any(), any(), any(), any(), any());
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", defaultMock);
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", defaultMock)));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {
"csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
verify(defaultMock).generateCertificateRequest(
@@ -215,8 +245,10 @@
when(defaultMock.generateCertificateRequestV2(any(), any()))
.thenReturn(new byte[] {0x68, 0x65, 0x6c, 0x6c, 0x6f});
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", defaultMock);
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", defaultMock)));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"csr", "default"});
verify(defaultMock).generateCertificateRequestV2(new MacedPublicKey[0], new byte[0]);
assertThat(res.getErr()).isEmpty();
@@ -233,8 +265,10 @@
when(defaultMock.generateCertificateRequestV2(any(), any()))
.thenReturn(new byte[] {0x68, 0x69});
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", defaultMock);
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", defaultMock)));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"csr", "--challenge", "dHJpYWw=", "default"});
verify(defaultMock).generateCertificateRequestV2(
new MacedPublicKey[0], new byte[] {0x74, 0x72, 0x69, 0x61, 0x6c});
@@ -242,4 +276,82 @@
assertThat(res.getCode()).isEqualTo(0);
assertThat(res.getOut()).isEqualTo("aGk=\n");
}
+
+ @Test
+ public void certify_sameOrderAsReceived() throws Exception {
+ String cert1 = "MIIBqDCCAU2gAwIBAgIUI3FFU7xZno/2Xf/wZzKKquP0ov0wCgYIKoZIzj0EAwIw\n"
+ + "KTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQKDARUZXN0MB4XDTIz\n"
+ + "MDgyMjE5MzgxMFoXDTMzMDgxOTE5MzgxMFowKTELMAkGA1UEBhMCVVMxCzAJBgNV\n"
+ + "BAgMAkNBMQ0wCwYDVQQKDARUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n"
+ + "czOpG6NKOdDjV/yrKjuy0q0jEJvsVLGgTeY+vyKRBJS59OhyRWG6n3aza21bNg5d\n"
+ + "WE9ruz+bcT0IP4kDbiS0y6NTMFEwHQYDVR0OBBYEFHYfJxCUipNI7qRqvczcWsOb\n"
+ + "FIDPMB8GA1UdIwQYMBaAFHYfJxCUipNI7qRqvczcWsObFIDPMA8GA1UdEwEB/wQF\n"
+ + "MAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAKm/kpJwlnWkjoLCAddBiSnxbT4EfJIK\n"
+ + "H0j58tg5VazHAiEAnS/kRzU9AbstOZyD7el/ws3gLXkbUNey3pLFutBWsSU=\n";
+ String cert2 = "MIIBpjCCAU2gAwIBAgIUdSzfZzeGr+h70JPO7Sxwdkw99iMwCgYIKoZIzj0EAwIw\n"
+ + "KTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQKDARUZXN0MB4XDTIz\n"
+ + "MDgyMjIwMTcyMFoXDTMzMDgxOTIwMTcyMFowKTELMAkGA1UEBhMCVVMxCzAJBgNV\n"
+ + "BAgMAkNBMQ0wCwYDVQQKDARUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n"
+ + "voGJi4DxuqH8rzPV6Eq0OVULc0xFzaM0500VBqiQEB7Qt0Ktk2d+3bUrFAb3SZV4\n"
+ + "6TIdb7SkynvaDtr0x45Ng6NTMFEwHQYDVR0OBBYEFMeGjvGV0ADPBJk5/FPoW9HQ\n"
+ + "uTc6MB8GA1UdIwQYMBaAFMeGjvGV0ADPBJk5/FPoW9HQuTc6MA8GA1UdEwEB/wQF\n"
+ + "MAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgd1gu7iiNOQXaQUn5BT3WwWR0Yk78ndWt\n"
+ + "ew7tRiTOhFcCIFURi6WcNH0oWa6IbwBSMC9aZlo98Fbg+dTwhLAAw+PW\n";
+ byte[] cert1Bytes = Base64.getDecoder().decode(cert1.replaceAll("\\s+", ""));
+ byte[] cert2Bytes = Base64.getDecoder().decode(cert2.replaceAll("\\s+", ""));
+ byte[] certChain = Arrays.copyOf(cert1Bytes, cert1Bytes.length + cert2Bytes.length);
+ System.arraycopy(cert2Bytes, 0, certChain, cert1Bytes.length, cert2Bytes.length);
+ RemotelyProvisionedKey keyMock = mock(RemotelyProvisionedKey.class);
+ when(keyMock.getEncodedCertChain()).thenReturn(certChain);
+ RegistrationProxy defaultMock = mock(RegistrationProxy.class);
+ doAnswer(invocation -> {
+ ((OutcomeReceiver<RemotelyProvisionedKey, Exception>) invocation.getArgument(3))
+ .onResult(keyMock);
+ return null;
+ }).when(defaultMock).getKeyAsync(anyInt(), any(), any(), any());
+
+ Injector injector = new Injector();
+ injector.mRegistrationProxies = Map.of("default", defaultMock);
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ mContext, Process.SHELL_UID, injector);
+ CommandResult res = exec(cmd, new String[] {"certify", "default"});
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ assertThat(res.getOut()).isEqualTo(
+ "-----BEGIN CERTIFICATE-----\n" + cert1 + "-----END CERTIFICATE-----\n"
+ + "-----BEGIN CERTIFICATE-----\n" + cert2 + "-----END CERTIFICATE-----\n");
+ }
+
+ @Test
+ public void certify_noBlankLineBeforeTrailer() throws Exception {
+ String cert = "MIIB2zCCAYGgAwIBAgIRAOpN7Em1k7gaqLAB2dzXUTYwCgYIKoZIzj0EAwIwKTET\n"
+ + "MBEGA1UEChMKR29vZ2xlIExMQzESMBAGA1UEAxMJRHJvaWQgQ0EzMB4XDTIzMDgx\n"
+ + "ODIzMzI1MloXDTIzMDkyMTIzMzI1MlowOTEMMAoGA1UEChMDVEVFMSkwJwYDVQQD\n"
+ + "EyBlYTRkZWM0OWI1OTNiODFhYThiMDAxZDlkY2Q3NTEzNjBZMBMGByqGSM49AgEG\n"
+ + "CCqGSM49AwEHA0IABHM/cKZblmlw8bdGbDXnX+ZiLiGjSjaLHXYOoHDrVArAMXUi\n"
+ + "L6brhcUPaqSGcVLcfFZbaFMOxXW6TsGdQiwJ0iyjejB4MB0GA1UdDgQWBBTYzft+\n"
+ + "X32TH/Hh+ngwQF6aPhnfXDAfBgNVHSMEGDAWgBQT4JObI9mzNNW2FRsHRcw4zVn2\n"
+ + "8jAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDAVBgorBgEEAdZ5AgEe\n"
+ + "BAehARoABAAAMAoGCCqGSM49BAMCA0gAMEUCIDc0OR7CzIYw0myTr0y/Brl1nZyk\n"
+ + "eGSQp615WpTwYhwxAiEApM10gSIKBIo7Z4/FNzkuiz1zZwW9+Dcqisqxkfe6icQ=\n";
+ byte[] certBytes = Base64.getDecoder().decode(cert.replaceAll("\\s+", ""));
+ RemotelyProvisionedKey keyMock = mock(RemotelyProvisionedKey.class);
+ when(keyMock.getEncodedCertChain()).thenReturn(certBytes);
+ RegistrationProxy defaultMock = mock(RegistrationProxy.class);
+ doAnswer(invocation -> {
+ ((OutcomeReceiver<RemotelyProvisionedKey, Exception>) invocation.getArgument(3))
+ .onResult(keyMock);
+ return null;
+ }).when(defaultMock).getKeyAsync(anyInt(), any(), any(), any());
+
+ Injector injector = new Injector();
+ injector.mRegistrationProxies = Map.of("strongbox", defaultMock);
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ mContext, Process.SHELL_UID, injector);
+ CommandResult res = exec(cmd, new String[] {"certify", "strongbox"});
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ assertThat(res.getOut()).isEqualTo(
+ "-----BEGIN CERTIFICATE-----\n" + cert + "-----END CERTIFICATE-----\n");
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index a6acd60..d16c37a4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -63,8 +63,10 @@
private static final float BRIGHTNESS_MAX_FLOAT = 1.0f;
private static final int LIGHT_SENSOR_RATE = 20;
private static final int INITIAL_LIGHT_SENSOR_RATE = 20;
- private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0;
- private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0;
+ private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 2000;
+ private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 4000;
+ private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 1000;
+ private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 2000;
private static final float DOZE_SCALE_FACTOR = 0.0f;
private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
@@ -96,8 +98,9 @@
mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
mContext = InstrumentationRegistry.getContext();
- mController = setupController(mLightSensor, BrightnessMappingStrategy.NO_USER_LUX,
- BrightnessMappingStrategy.NO_USER_BRIGHTNESS);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ false,
+ /* useHorizon= */ true);
}
@After
@@ -109,12 +112,12 @@
}
}
- private AutomaticBrightnessController setupController(Sensor lightSensor, float userLux,
- float userBrightness) {
+ private void setupController(float userLux, float userBrightness, boolean applyDebounce,
+ boolean useHorizon) {
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
- AutomaticBrightnessController controller = new AutomaticBrightnessController(
+ mController = new AutomaticBrightnessController(
new AutomaticBrightnessController.Injector() {
@Override
public Handler getBackgroundThreadHandler() {
@@ -127,16 +130,19 @@
}
}, // pass in test looper instead, pass in offsettable clock
- () -> { }, mTestLooper.getLooper(), mSensorManager, lightSensor,
+ () -> { }, mTestLooper.getLooper(), mSensorManager, mLightSensor,
mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
- INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
- DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
+ INITIAL_LIGHT_SENSOR_RATE, applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG : 0,
+ applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG : 0,
+ applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
+ applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
+ RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
mContext, mBrightnessRangeController, mBrightnessThrottler,
- mIdleBrightnessMappingStrategy, AMBIENT_LIGHT_HORIZON_SHORT,
- AMBIENT_LIGHT_HORIZON_LONG, userLux, userBrightness
+ mIdleBrightnessMappingStrategy, useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
+ useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userBrightness
);
when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
@@ -149,12 +155,10 @@
// Configure the brightness controller and grab an instance of the sensor listener,
// through which we can deliver fake (for test) sensor values.
- controller.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0 /* brightness= */, false /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
/* shouldResetShortTermModel= */ true);
-
- return controller;
}
@Test
@@ -536,8 +540,10 @@
// Now let's do the same for idle mode
mController.switchToIdleMode();
// Called once for init, and once when switching,
- // setAmbientLux() is called twice and once in updateAutoBrightness()
- verify(mBrightnessMappingStrategy, times(5)).isForIdleMode();
+ // setAmbientLux() is called twice and once in updateAutoBrightness(),
+ // nextAmbientLightBrighteningTransition() and nextAmbientLightDarkeningTransition() are
+ // called twice each.
+ verify(mBrightnessMappingStrategy, times(9)).isForIdleMode();
// Called when switching.
verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout();
verify(mBrightnessMappingStrategy, times(1)).getUserBrightness();
@@ -838,7 +844,158 @@
float userLux = 1000;
float userBrightness = 0.3f;
- setupController(mLightSensor, userLux, userBrightness);
+ setupController(userLux, userBrightness, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
verify(mBrightnessMappingStrategy).addUserDataPoint(userLux, userBrightness);
}
+
+ @Test
+ public void testBrighteningLightDebounce() throws Exception {
+ clearInvocations(mSensorManager);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // t = 0
+ // Initial lux
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 1000
+ // Lux isn't steady yet
+ mClock.fastForward(1000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 1500
+ // Lux isn't steady yet
+ mClock.fastForward(500);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 2500
+ // Lux is steady now
+ mClock.fastForward(1000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+ }
+
+ @Test
+ public void testDarkeningLightDebounce() throws Exception {
+ clearInvocations(mSensorManager);
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(anyFloat()))
+ .thenReturn(10000f);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(anyFloat()))
+ .thenReturn(10000f);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // t = 0
+ // Initial lux
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 2000
+ // Lux isn't steady yet
+ mClock.fastForward(2000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 2500
+ // Lux isn't steady yet
+ mClock.fastForward(500);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 4500
+ // Lux is steady now
+ mClock.fastForward(2000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+ }
+
+ @Test
+ public void testBrighteningLightDebounceIdle() throws Exception {
+ clearInvocations(mSensorManager);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
+
+ mController.switchToIdleMode();
+ when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // t = 0
+ // Initial lux
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 500
+ // Lux isn't steady yet
+ mClock.fastForward(500);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 1500
+ // Lux is steady now
+ mClock.fastForward(1000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+ }
+
+ @Test
+ public void testDarkeningLightDebounceIdle() throws Exception {
+ clearInvocations(mSensorManager);
+ when(mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(anyFloat()))
+ .thenReturn(10000f);
+ when(mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(anyFloat()))
+ .thenReturn(10000f);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
+
+ mController.switchToIdleMode();
+ when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // t = 0
+ // Initial lux
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 1000
+ // Lux isn't steady yet
+ mClock.fastForward(1000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 2500
+ // Lux is steady now
+ mClock.fastForward(1500);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java b/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
index 53d8de0c..3c3ef9a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
@@ -25,12 +25,9 @@
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-import com.android.server.LocalServices;
-
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,25 +46,13 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
mContext = getInstrumentation().getTargetContext();
}
- @After
- public void tearDown() {
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- }
-
@Test
public void testPrepareColorFadeForInvalidDisplay() {
when(mDisplayManagerInternalMock.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(null);
- ColorFade colorFade = new ColorFade(DISPLAY_ID);
+ ColorFade colorFade = new ColorFade(DISPLAY_ID, mDisplayManagerInternalMock);
assertFalse(colorFade.prepare(mContext, ColorFade.MODE_FADE));
}
-
- private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
- LocalServices.removeServiceForTest(clazz);
- LocalServices.addService(clazz, mock);
- }
-
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index d9338a9..82d00a6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -112,8 +112,6 @@
assertArrayEquals(mDisplayDeviceConfig.getBrightness(), BRIGHTNESS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getNits(), NITS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getBacklight(), BRIGHTNESS, ZERO_DELTA);
- assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
- assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
float[]{0.0f, 50.0f, 80.0f}, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
@@ -413,6 +411,17 @@
assertArrayEquals(new int[]{ -1, 10, 20, 30, 40 },
mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux());
+
+ // Low/High zone thermal maps
+ assertEquals(new SurfaceControl.RefreshRateRange(30, 40),
+ mDisplayDeviceConfig.getLowBlockingZoneThermalMap()
+ .get(Temperature.THROTTLING_CRITICAL));
+ assertEquals(new SurfaceControl.RefreshRateRange(40, 60),
+ mDisplayDeviceConfig.getHighBlockingZoneThermalMap()
+ .get(Temperature.THROTTLING_EMERGENCY));
+
+ // Todo: Add asserts for DensityMapping,
+ // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
}
@Test
@@ -540,6 +549,16 @@
assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
}
+ @Test
+ public void testLightDebounceFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounceIdle(), 2500);
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounceIdle(), 1500);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -632,6 +651,24 @@
+ " </refreshRateRange>\n"
+ " </refreshRateThrottlingPoint>\n"
+ "</refreshRateThrottlingMap>\n"
+ + "<refreshRateThrottlingMap id=\"thermalLow\">\n"
+ + " <refreshRateThrottlingPoint>\n"
+ + " <thermalStatus>critical</thermalStatus>\n"
+ + " <refreshRateRange>\n"
+ + " <minimum>30</minimum>\n"
+ + " <maximum>40</maximum>\n"
+ + " </refreshRateRange>\n"
+ + " </refreshRateThrottlingPoint>\n"
+ + "</refreshRateThrottlingMap>\n"
+ + "<refreshRateThrottlingMap id=\"thermalHigh\">\n"
+ + " <refreshRateThrottlingPoint>\n"
+ + " <thermalStatus>emergency</thermalStatus>\n"
+ + " <refreshRateRange>\n"
+ + " <minimum>40</minimum>\n"
+ + " <maximum>60</maximum>\n"
+ + " </refreshRateRange>\n"
+ + " </refreshRateThrottlingPoint>\n"
+ + "</refreshRateThrottlingMap>\n"
+ "<refreshRateThrottlingMap id=\"test\">\n"
+ " <refreshRateThrottlingPoint>\n"
+ " <thermalStatus>emergency</thermalStatus>\n"
@@ -704,6 +741,12 @@
+ "<autoBrightness>\n"
+ "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
+ "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
+ + "<brighteningLightDebounceIdleMillis>"
+ + "2500"
+ + "</brighteningLightDebounceIdleMillis>\n"
+ + "<darkeningLightDebounceIdleMillis>"
+ + "1500"
+ + "</darkeningLightDebounceIdleMillis>\n"
+ "<displayBrightnessMapping>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>50</lux>\n"
@@ -969,6 +1012,8 @@
+ "<defaultRefreshRateInHbmSunlight>83</defaultRefreshRateInHbmSunlight>\n"
+ "<lowerBlockingZoneConfigs>\n"
+ "<defaultRefreshRate>75</defaultRefreshRate>\n"
+ + "<refreshRateThermalThrottlingId>thermalLow"
+ + "</refreshRateThermalThrottlingId>\n"
+ "<blockingZoneThreshold>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>50</lux>\n"
@@ -990,6 +1035,8 @@
+ "</lowerBlockingZoneConfigs>\n"
+ "<higherBlockingZoneConfigs>\n"
+ "<defaultRefreshRate>90</defaultRefreshRate>\n"
+ + "<refreshRateThermalThrottlingId>thermalHigh"
+ + "</refreshRateThermalThrottlingId>\n"
+ "<blockingZoneThreshold>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>70</lux>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index 4c3d80a..4cbdd09 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -1061,6 +1061,8 @@
anyInt(),
anyLong(),
anyLong(),
+ anyLong(),
+ anyLong(),
anyBoolean(),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
@@ -1350,6 +1352,7 @@
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 01e49f2..68bbcbc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -63,8 +63,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.util.test.LocalServiceKeeperRule;
import com.android.modules.utils.testing.ExtendedMockitoRule;
-import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
@@ -74,7 +74,6 @@
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.testutils.OffsettableClock;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -140,6 +139,9 @@
.spyStatic(BatteryStatsService.class)
.build();
+ @Rule
+ public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
@Before
public void setUp() throws Exception {
mClock = new OffsettableClock.Stopped();
@@ -153,9 +155,10 @@
Settings.System.putFloatForUser(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT);
- addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
- addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
- mCdsiMock);
+ mLocalServiceKeeperRule.overrideLocalService(
+ WindowManagerPolicy.class, mWindowManagerPolicyMock);
+ mLocalServiceKeeperRule.overrideLocalService(
+ ColorDisplayService.ColorDisplayServiceInternal.class, mCdsiMock);
mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
@@ -167,12 +170,6 @@
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
}
- @After
- public void tearDown() {
- LocalServices.removeServiceForTest(WindowManagerPolicy.class);
- LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
- }
-
@Test
public void testReleaseProxSuspendBlockersOnExit() throws Exception {
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
@@ -1071,6 +1068,8 @@
anyInt(),
anyLong(),
anyLong(),
+ anyLong(),
+ anyLong(),
anyBoolean(),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
@@ -1124,14 +1123,6 @@
verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true);
}
- /**
- * Creates a mock and registers it to {@link LocalServices}.
- */
- private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
- LocalServices.removeServiceForTest(clazz);
- LocalServices.addService(clazz, mock);
- }
-
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
@@ -1330,6 +1321,7 @@
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index b088dbf..168eefc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -39,6 +39,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -1093,6 +1094,196 @@
}
@Test
+ public void testLockFpsForHighZoneWithThermalCondition() throws Exception {
+ // First, configure brightness zones or DMD won't register for sensor data.
+ final FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setHighDisplayBrightnessThresholds(new int[] { 200 });
+ config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0);
+ setPeakRefreshRate(120 /*fps*/);
+ director.getSettingsObserver().setDefaultRefreshRate(120);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ // Set the thresholds for High Zone
+ DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
+ when(ddcMock.getDefaultHighBlockingZoneRefreshRate()).thenReturn(90);
+ when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 });
+ when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 });
+ when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
+ when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] {});
+ when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] {});
+
+ // Set the thermal condition for refresh rate range
+ when(ddcMock.getHighBlockingZoneThermalMap()).thenReturn(
+ new SparseArray<RefreshRateRange>() {{
+ put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60));
+ }}
+ );
+ director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ // Get the display listener so that we can send it new brightness events
+ ArgumentCaptor<DisplayListener> displayListenerCaptor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ DisplayListener displayListener = displayListenerCaptor.getValue();
+
+ // Get the sensor listener so that we can give it new light sensor events
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+ SensorEventListener sensorListener = listenerCaptor.getValue();
+
+ // Get the thermal listener so that we can give it new thermal conditions
+ ArgumentCaptor<IThermalEventListener> thermalListenerCaptor =
+ ArgumentCaptor.forClass(IThermalEventListener.class);
+ verify(mInjector, atLeastOnce()).registerThermalServiceListener(
+ thermalListenerCaptor.capture());
+ List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues();
+
+ setBrightness(100, 100, displayListener);
+ // Sensor reads 2000 lux,
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertThat(vote).isNull();
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNull();
+
+ // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
+ // parameter to the necessary threshold
+ setBrightness(255, 255, displayListener);
+ // Sensor reads 9000 lux,
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
+
+ // Set critical and check new refresh rate
+ Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
+ for (var listener : thermalListeners) {
+ listener.notifyThrottling(temp);
+ }
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
+ }
+
+ @Test
+ public void testLockFpsForLowZoneWithThermalCondition() throws Exception {
+ // First, configure brightness zones or DMD won't register for sensor data.
+ final FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setHighDisplayBrightnessThresholds(new int[] { 200 });
+ config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0);
+ setPeakRefreshRate(120 /*fps*/);
+ director.getSettingsObserver().setDefaultRefreshRate(120);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ // Set the thresholds for Low Zone
+ DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
+ when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
+ when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 });
+ when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 });
+ when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
+ when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] { 10 });
+ when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] { 10 });
+
+ // Set the thermal condition for refresh rate range
+ when(ddcMock.getLowBlockingZoneThermalMap()).thenReturn(
+ new SparseArray<RefreshRateRange>() {{
+ put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60));
+ }}
+ );
+ director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ // Get the display listener so that we can send it new brightness events
+ ArgumentCaptor<DisplayListener> displayListenerCaptor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ DisplayListener displayListener = displayListenerCaptor.getValue();
+
+ // Get the sensor listener so that we can give it new light sensor events
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+ SensorEventListener sensorListener = listenerCaptor.getValue();
+
+ // Get the thermal listener so that we can give it new thermal conditions
+ ArgumentCaptor<IThermalEventListener> thermalListenerCaptor =
+ ArgumentCaptor.forClass(IThermalEventListener.class);
+ verify(mInjector, atLeastOnce()).registerThermalServiceListener(
+ thermalListenerCaptor.capture());
+ List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues();
+
+ setBrightness(100, 100, displayListener);
+ // Sensor reads 2000 lux,
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertThat(vote).isNull();
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNull();
+
+ // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
+ // parameter to the necessary threshold
+ setBrightness(5, 5, displayListener);
+ // Sensor reads 9 lux,
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9));
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
+
+ // Set critical and check new refresh rate
+ Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
+ for (var listener : thermalListeners) {
+ listener.notifyThrottling(temp);
+ }
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
+ }
+
+ @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -2907,6 +3098,10 @@
}
@Override
+ public void unregisterThermalServiceListener(IThermalEventListener listener) {
+ }
+
+ @Override
public boolean supportsFrameRateOverride() {
return true;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index 183a84d..f9fbf1b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -38,7 +38,7 @@
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
-import com.android.server.LocalServices;
+import com.android.internal.util.test.LocalServiceKeeperRule;
import com.android.server.display.TestUtils;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.display.utils.AmbientFilter;
@@ -47,6 +47,7 @@
import com.google.common.collect.ImmutableList;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -90,6 +91,9 @@
@Mock private TypedArray mStrongDisplayColorTemperatures;
@Mock private ColorDisplayService.ColorDisplayServiceInternal mColorDisplayServiceInternalMock;
+ @Rule
+ public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -156,8 +160,9 @@
R.array.config_displayWhiteBalanceHighLightAmbientBiasesStrong))
.thenReturn(mHighLightBiasesStrong);
mockThrottler();
- LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
- LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
+
+ mLocalServiceKeeperRule.overrideLocalService(
+ ColorDisplayService.ColorDisplayServiceInternal.class,
mColorDisplayServiceInternalMock);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index cffd027..6c39275 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -131,7 +131,7 @@
wheneverStatic { HexEncoding.decode(STATIC_LIB_NAME, false) }
.thenReturn(PackageUtils.computeSha256DigestBytes(
mSettings.getPackageLPr(STATIC_LIB_PACKAGE_NAME)
- .pkg.signingDetails.signatures!![0].toByteArray()))
+ .pkg!!.signingDetails.signatures!![0].toByteArray()))
}
@Test
@@ -239,7 +239,7 @@
testPackageSetting.setPkgStateLibraryFiles(listOf())
assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
- mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg, testPackageSetting,
+ mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg!!, testPackageSetting,
null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
@@ -252,7 +252,7 @@
testPackageSetting.setPkgStateLibraryFiles(listOf())
assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
- mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg, testPackageSetting,
+ mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg!!, testPackageSetting,
null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
@@ -266,7 +266,7 @@
testPackageSetting.setPkgStateLibraryFiles(listOf())
assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
- mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg, testPackageSetting,
+ mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg!!, testPackageSetting,
null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
assertThat(testPackageSetting.usesLibraryFiles).hasSize(3)
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index 746fb53..64e776e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics;
+import static com.android.server.biometrics.AuthenticationStatsCollector.MAXIMUM_ENROLLMENT_NOTIFICATIONS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -44,9 +46,11 @@
import com.android.server.biometrics.sensors.BiometricNotification;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.io.File;
@@ -54,6 +58,9 @@
@SmallTest
public class AuthenticationStatsCollectorTest {
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
private AuthenticationStatsCollector mAuthenticationStatsCollector;
private static final float FRR_THRESHOLD = 0.2f;
private static final int USER_ID_1 = 1;
@@ -75,7 +82,6 @@
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getFraction(eq(R.fraction.config_biometricNotificationFrrThreshold),
@@ -130,6 +136,33 @@
assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
}
+ /**
+ * Our current use case does not need the counters to update after the notification
+ * limit is reached. If we need these counters to continue counting in the future,
+ * a separate privacy review must be done.
+ */
+ @Test
+ public void authenticate_notificationExceeded_mapMustNotBeUpdated() {
+
+ mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1,
+ new AuthenticationStats(USER_ID_1, 400 /* totalAttempts */,
+ 40 /* rejectedAttempts */,
+ MAXIMUM_ENROLLMENT_NOTIFICATIONS /* enrollmentNotifications */,
+ 0 /* modality */));
+
+ mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
+
+ AuthenticationStats authenticationStats =
+ mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1);
+
+ assertThat(authenticationStats.getUserId()).isEqualTo(USER_ID_1);
+ // Assert that counters haven't been updated.
+ assertThat(authenticationStats.getTotalAttempts()).isEqualTo(400);
+ assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(40);
+ assertThat(authenticationStats.getEnrollmentNotifications())
+ .isEqualTo(MAXIMUM_ENROLLMENT_NOTIFICATIONS);
+ }
+
@Test
public void authenticate_frrNotExceeded_notificationNotExceeded_shouldNotSendNotification() {
@@ -156,7 +189,8 @@
mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1,
new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */,
- 400 /* rejectedAttempts */, 2 /* enrollmentNotifications */,
+ 400 /* rejectedAttempts */,
+ MAXIMUM_ENROLLMENT_NOTIFICATIONS /* enrollmentNotifications */,
0 /* modality */));
mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
@@ -164,12 +198,12 @@
// Assert that no notification should be sent.
verify(mBiometricNotification, never()).sendFaceEnrollNotification(any());
verify(mBiometricNotification, never()).sendFpEnrollNotification(any());
- // Assert that data has been reset.
+ // Assert that data hasn't been reset.
AuthenticationStats authenticationStats = mAuthenticationStatsCollector
.getAuthenticationStatsForUser(USER_ID_1);
- assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
- assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
- assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
+ assertThat(authenticationStats.getTotalAttempts()).isEqualTo(500);
+ assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(400);
+ assertThat(authenticationStats.getFrr()).isWithin(0f).of(0.8f);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 908afc8..00e35ec 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -226,6 +226,7 @@
private VirtualDeviceManagerService mVdms;
private VirtualDeviceManagerInternal mLocalService;
private VirtualDeviceManagerService.VirtualDeviceManagerImpl mVdm;
+ private VirtualDeviceLog mVirtualDeviceLog;
@Mock
private InputController.NativeWrapper mNativeWrapperMock;
@Mock
@@ -370,6 +371,7 @@
mVdms = new VirtualDeviceManagerService(mContext);
mLocalService = mVdms.getLocalServiceInstance();
mVdm = mVdms.new VirtualDeviceManagerImpl();
+ mVirtualDeviceLog = new VirtualDeviceLog(mContext);
mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1);
mSensorController = mDeviceImpl.getSensorControllerForTest();
}
@@ -1730,7 +1732,7 @@
private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid,
VirtualDeviceParams params) {
VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext,
- mAssociationInfo, mVdms, new Binder(),
+ mAssociationInfo, mVdms, mVirtualDeviceLog, new Binder(),
new AttributionSource(ownerUid, "com.android.virtualdevice.test", "virtualdevice"),
virtualDeviceId,
mInputController, mCameraAccessController,
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index ee3ab97..9fca513 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -149,32 +149,6 @@
}
@Test
- public void testCreateHintSession_exceedsLimit() throws Exception {
- HintManagerService service = createService();
- IBinder token1 = new Binder();
- IBinder token2 = new Binder();
-
- for (int i = 0; i < 10; i++) {
- IHintSession a = service.getBinderServiceInstance().createHintSession(token1,
- SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
- assertNotNull(a);
- }
-
- for (int i = 0; i < 10; i++) {
- IHintSession b = service.getBinderServiceInstance().createHintSession(token2,
- SESSION_TIDS_B, DEFAULT_TARGET_DURATION);
- assertNotNull(b);
- }
-
- assertThrows(IllegalStateException.class,
- () -> service.getBinderServiceInstance().createHintSession(token1, SESSION_TIDS_A,
- DEFAULT_TARGET_DURATION));
- assertThrows(IllegalStateException.class,
- () -> service.getBinderServiceInstance().createHintSession(token2, SESSION_TIDS_B,
- DEFAULT_TARGET_DURATION));
- }
-
- @Test
public void testPauseResumeHintSession() throws Exception {
HintManagerService service = createService();
IBinder token = new Binder();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 579bbc8..37f4983 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6045,31 +6045,33 @@
@Test
public void testVisitUris_styleExtrasWithoutStyle() {
- Notification notification = new Notification.Builder(mContext, "a")
- .setSmallIcon(android.R.drawable.sym_def_app_icon)
- .build();
+ Notification.Builder notification = new Notification.Builder(mContext, "a")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
- Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(
- personWithIcon("content://user"))
- .addHistoricMessage(new Notification.MessagingStyle.Message("Heyhey!",
- System.currentTimeMillis(),
- personWithIcon("content://historicalMessenger")))
- .addMessage(new Notification.MessagingStyle.Message("Are you there",
- System.currentTimeMillis(),
- personWithIcon("content://messenger")))
- .setShortcutIcon(
- Icon.createWithContentUri("content://conversationShortcut"));
- messagingStyle.addExtras(notification.extras); // Instead of Builder.setStyle(style).
+ Bundle messagingExtras = new Bundle();
+ messagingExtras.putParcelable(Notification.EXTRA_MESSAGING_PERSON,
+ personWithIcon("content://user"));
+ messagingExtras.putParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES,
+ new Bundle[] { new Notification.MessagingStyle.Message("Heyhey!",
+ System.currentTimeMillis() - 100,
+ personWithIcon("content://historicalMessenger")).toBundle()});
+ messagingExtras.putParcelableArray(Notification.EXTRA_MESSAGES,
+ new Bundle[] { new Notification.MessagingStyle.Message("Are you there?",
+ System.currentTimeMillis(),
+ personWithIcon("content://messenger")).toBundle()});
+ messagingExtras.putParcelable(Notification.EXTRA_CONVERSATION_ICON,
+ Icon.createWithContentUri("content://conversationShortcut"));
+ notification.addExtras(messagingExtras);
- Notification.CallStyle callStyle = Notification.CallStyle.forOngoingCall(
- personWithIcon("content://caller"),
- PendingIntent.getActivity(mContext, 0, new Intent(),
- PendingIntent.FLAG_IMMUTABLE))
- .setVerificationIcon(Icon.createWithContentUri("content://callVerification"));
- callStyle.addExtras(notification.extras); // Same.
+ Bundle callExtras = new Bundle();
+ callExtras.putParcelable(Notification.EXTRA_CALL_PERSON,
+ personWithIcon("content://caller"));
+ callExtras.putParcelable(Notification.EXTRA_VERIFICATION_ICON,
+ Icon.createWithContentUri("content://callVerification"));
+ notification.addExtras(callExtras);
Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
- notification.visitUris(visitor);
+ notification.build().visitUris(visitor);
verify(visitor).accept(eq(Uri.parse("content://user")));
verify(visitor).accept(eq(Uri.parse("content://historicalMessenger")));
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 81d939f..9b745f5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -115,6 +115,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
+import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -209,6 +210,7 @@
@Mock Context mContext;
@Mock ZenModeHelper mMockZenModeHelper;
@Mock AppOpsManager mAppOpsManager;
+ @Mock ManagedServices.UserProfiles mUserProfiles;
@Mock PermissionManager mPermissionManager;
private NotificationManager.Policy mTestNotificationPolicy;
@@ -327,10 +329,12 @@
when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
.thenReturn(appPermissions);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
+
mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager,
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
mStatsEventBuilderFactory, false);
resetZenModeHelper();
@@ -678,7 +682,8 @@
@Test
public void testReadXml_oldXml_migrates() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"2\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
@@ -743,9 +748,6 @@
@Test
public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
when(mPm.getPackageUidAsUser("pkg3", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
@@ -822,7 +824,8 @@
@Test
public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -879,7 +882,8 @@
@Test
public void testReadXml_newXml_permissionNotificationOff() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ false);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -936,7 +940,8 @@
@Test
public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"4\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -991,9 +996,6 @@
@Test
public void testReadXml_oldXml_migration_NoUid() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"2\">\n"
+ "<package name=\"something\" show_badge=\"true\">\n"
@@ -1024,9 +1026,6 @@
@Test
public void testReadXml_newXml_noMigration_NoUid() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"something\" show_badge=\"true\">\n"
@@ -1056,9 +1055,6 @@
@Test
public void testChannelXmlForNonBackup_postMigration() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
appPermissions.put(new Pair<>(3, "third"), new Pair<>(false, false));
@@ -1142,9 +1138,6 @@
@Test
public void testChannelXmlForBackup_postMigration() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
appPermissions.put(new Pair<>(3, "third"), new Pair<>(false, false));
@@ -1234,9 +1227,6 @@
@Test
public void testChannelXmlForBackup_postMigration_noExternal() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(UID_P, PKG_P), new Pair<>(true, false));
appPermissions.put(new Pair<>(UID_O, PKG_O), new Pair<>(false, false));
@@ -1319,9 +1309,6 @@
@Test
public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
appPermissions.put(new Pair<>(3, "third"), new Pair<>(false, false));
@@ -1533,7 +1520,8 @@
new FileNotFoundException("")).thenReturn(resId);
mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, false);
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -2477,9 +2465,6 @@
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.syncChannelsBypassingDnd();
// create notification channel that can bypass dnd, but app is blocked
@@ -2508,9 +2493,6 @@
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.syncChannelsBypassingDnd();
// create notification channel that can bypass dnd, but app is blocked
@@ -2533,9 +2515,6 @@
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.syncChannelsBypassingDnd();
// create notification channel that can bypass dnd, but app is blocked
@@ -2588,9 +2567,6 @@
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.syncChannelsBypassingDnd();
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
@@ -2602,15 +2578,58 @@
// start notification policy off with mAreChannelsBypassingDnd = false
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
resetZenModeHelper();
}
@Test
+ public void syncChannelsBypassingDnd_includesProfilesOfCurrentUser() throws Exception {
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0, 10}));
+ when(mPermissionHelper.hasPermission(anyInt())).thenReturn(true);
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+ when(mPm.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(appInfo);
+
+ NotificationChannel withBypass = new NotificationChannel("1", "with", IMPORTANCE_DEFAULT);
+ withBypass.setBypassDnd(true);
+ NotificationChannel withoutBypass = new NotificationChannel("2", "without",
+ IMPORTANCE_DEFAULT);
+ withoutBypass.setBypassDnd(false);
+ mHelper.createNotificationChannel("com.example", UserHandle.getUid(0, 444), withoutBypass,
+ false, false, Process.SYSTEM_UID, true);
+ mHelper.createNotificationChannel("com.example", UserHandle.getUid(10, 444), withBypass,
+ false, false, Process.SYSTEM_UID, true);
+
+ mHelper.syncChannelsBypassingDnd();
+
+ assertThat(mHelper.areChannelsBypassingDnd()).isTrue();
+ }
+
+ @Test
+ public void syncChannelsBypassingDnd_excludesOtherUsers() throws Exception {
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
+ when(mPermissionHelper.hasPermission(anyInt())).thenReturn(true);
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+ when(mPm.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(appInfo);
+
+ NotificationChannel withBypass = new NotificationChannel("1", "with", IMPORTANCE_DEFAULT);
+ withBypass.setBypassDnd(true);
+ NotificationChannel withoutBypass = new NotificationChannel("2", "without",
+ IMPORTANCE_DEFAULT);
+ withoutBypass.setBypassDnd(false);
+ mHelper.createNotificationChannel("com.example", UserHandle.getUid(0, 444), withoutBypass,
+ false, false, Process.SYSTEM_UID, true);
+ mHelper.createNotificationChannel("com.example", UserHandle.getUid(10, 444), withBypass,
+ false, false, Process.SYSTEM_UID, true);
+
+ mHelper.syncChannelsBypassingDnd();
+
+ assertThat(mHelper.areChannelsBypassingDnd()).isFalse();
+ }
+
+ @Test
public void testCreateDeletedChannel() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
@@ -3705,9 +3724,7 @@
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ "</package>\n"
+ "</ranking>\n";
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+
loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM);
assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3719,9 +3736,6 @@
mHelper.setHideSilentStatusIcons(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3789,9 +3803,6 @@
mHelper.canShowBadge(PKG_O, UID_O);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3802,9 +3813,6 @@
mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3816,9 +3824,6 @@
mHelper.revokeNotificationDelegate(PKG_O, UID_O);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3832,9 +3837,6 @@
assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3888,9 +3890,6 @@
mHelper.getAppLockedFields(PKG_O, UID_O));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3926,9 +3925,6 @@
mHelper.getAppLockedFields(PKG_O, UID_O));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
@@ -4584,10 +4580,6 @@
@Test
public void testPlaceholderConversationId_shortcutRequired() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
@@ -4604,10 +4596,6 @@
@Test
public void testNormalConversationId_shortcutRequired() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>"
@@ -4624,10 +4612,6 @@
@Test
public void testNoConversationId_shortcutRequired() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\"/>"
@@ -4644,10 +4628,6 @@
@Test
public void testDeleted_noTime() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\" deleted=\"true\"/>"
@@ -4664,13 +4644,10 @@
@Test
public void testDeleted_twice() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
UID_P, false);
+
assertTrue(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id",
UID_P, false));
assertFalse(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id",
@@ -4679,10 +4656,6 @@
@Test
public void testDeleted_recentTime() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
UID_P, false);
@@ -4698,9 +4671,6 @@
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
null);
parser.nextTag();
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.readXml(parser, true, USER_SYSTEM);
NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
@@ -4710,10 +4680,6 @@
@Test
public void testUnDelete_time() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
UID_P, false);
@@ -4732,10 +4698,6 @@
@Test
public void testDeleted_longTime() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30);
final String xml = "<ranking version=\"1\">\n"
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
index 6f65406..05a1482 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -112,7 +112,7 @@
// Show assistant.
mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_ASSISTANT);
sendKey(KEYCODE_POWER, true);
- mPhoneWindowManager.assertAssistLaunch();
+ mPhoneWindowManager.assertSearchManagerLaunchAssist();
// Show global actions.
mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_GLOBAL_ACTIONS);
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index c433e64..eab8757 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -16,11 +16,15 @@
package com.android.server.policy;
+import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
+import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY;
+import android.content.ComponentName;
import android.provider.Settings;
import org.junit.Test;
@@ -32,6 +36,9 @@
* atest WmTests:StemKeyGestureTests
*/
public class StemKeyGestureTests extends ShortcutKeyTestBase {
+
+ private static final String TEST_TARGET_ACTIVITY = "com.android.server.policy/.TestActivity";
+
/**
* Stem single key should not launch behavior during set up.
*/
@@ -63,6 +70,57 @@
mPhoneWindowManager.assertOpenAllAppView();
}
+ /**
+ * Stem single key should not launch behavior during set up.
+ */
+ @Test
+ public void stemSingleKey_launchTargetActivity() {
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_SHORT_PRESS,
+ SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideStartActivity();
+ mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+ mPhoneWindowManager.assumeResolveActivityNotNull();
+
+ ComponentName targetComponent = ComponentName.unflattenFromString(TEST_TARGET_ACTIVITY);
+ mPhoneWindowManager.overrideStemPressTargetActivity(targetComponent);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertActivityTargetLaunched(targetComponent);
+ }
+
+ @Test
+ public void stemLongKey_triggerSearchServiceToLaunchAssist() {
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_LONG_PRESS,
+ LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.setupAssistForLaunch();
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+ sendKey(KEYCODE_STEM_PRIMARY, /* longPress= */ true);
+ mPhoneWindowManager.assertSearchManagerLaunchAssist();
+ }
+
+ @Test
+ public void stemLongKey_whenNoSearchService_triggerStatusBarToStartAssist() {
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_LONG_PRESS,
+ LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.setupAssistForLaunch();
+ mPhoneWindowManager.overrideSearchManager(null);
+ mPhoneWindowManager.overrideStatusBarManagerInternal();
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+ sendKey(KEYCODE_STEM_PRIMARY, /* longPress= */ true);
+ mPhoneWindowManager.assertStatusBarStartAssist();
+ }
+
+
private void overrideBehavior(String key, int expectedBehavior) {
Settings.Global.putLong(mContext.getContentResolver(), key, expectedBehavior);
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index ef3a6ed..bc8f06a 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -59,9 +59,11 @@
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.SearchManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.hardware.SensorPrivacyManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
@@ -230,6 +232,7 @@
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mSensorPrivacyManager).when(mContext).getSystemService(
eq(SensorPrivacyManager.class));
+ doReturn(mSearchManager).when(mContext).getSystemService(eq(SearchManager.class));
doReturn(false).when(mPackageManager).hasSystemFeature(any());
try {
doThrow(new PackageManager.NameNotFoundException("test")).when(mPackageManager)
@@ -355,11 +358,7 @@
case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
break;
case LONG_PRESS_POWER_ASSISTANT:
- doNothing().when(mPhoneWindowManager).sendCloseSystemWindows();
- doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
- doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
- doReturn(mSearchManager).when(mContext)
- .getSystemService(eq(Context.SEARCH_SERVICE));
+ setupAssistForLaunch();
mPhoneWindowManager.mLongPressOnPowerAssistantTimeoutMs = 500;
break;
}
@@ -433,6 +432,22 @@
doReturn(isShowing).when(mKeyguardServiceDelegate).isShowing();
}
+ void setupAssistForLaunch() {
+ doNothing().when(mPhoneWindowManager).sendCloseSystemWindows();
+ doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
+ doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+ }
+
+ void overrideSearchManager(SearchManager searchManager) {
+ mPhoneWindowManager.mSearchManager = searchManager;
+ }
+
+ void assumeResolveActivityNotNull() {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ doReturn(resolveInfo).when(mPackageManager).resolveActivity(any(), anyInt());
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ }
+
void overrideKeyEventSource(int vendorId, int productId) {
InputDevice device = new InputDevice.Builder().setId(1).setVendorId(vendorId).setProductId(
productId).setSources(InputDevice.SOURCE_KEYBOARD).setKeyboardType(
@@ -458,6 +473,10 @@
doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
}
+ void overrideStemPressTargetActivity(ComponentName component) {
+ mPhoneWindowManager.mPrimaryShortPressTargetActivity = component;
+ }
+
/**
* Below functions will check the policy behavior could be invoked.
*/
@@ -517,7 +536,7 @@
.interceptPowerKeyDown(any(), anyBoolean(), any());
}
- void assertAssistLaunch() {
+ void assertSearchManagerLaunchAssist() {
waitForIdle();
verify(mSearchManager, timeout(SHORTCUT_KEY_DELAY_MILLIS)).launchAssist(any());
}
@@ -540,6 +559,11 @@
verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
}
+ void assertStatusBarStartAssist() {
+ waitForIdle();
+ verify(mStatusBarManagerInternal).startAssist(any());
+ }
+
void assertSwitchKeyboardLayout(int direction) {
waitForIdle();
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
@@ -613,6 +637,14 @@
.startActivityAsUser(any(Intent.class), any(), any(UserHandle.class));
}
+ void assertActivityTargetLaunched(ComponentName targetActivity) {
+ waitForIdle();
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
+ .startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
+ Assert.assertEquals(targetActivity, intentCaptor.getValue().getComponent());
+ }
+
void assertShortcutLogged(int vendorId, int productId, KeyboardLogEvent logEvent,
int expectedKey, int expectedModifierState, String errorMsg) {
waitForIdle();
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index befc4a0..4c978ad 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -259,7 +259,24 @@
// NOTE: No permissions required
if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
- return FileUtils.roundStorageSize(mStorage.getPrimaryStorageSize());
+ // As a safety measure, use the original implementation for the devices
+ // with storage size <= 512GB to prevent any potential regressions
+ final long roundedUserspaceBytes = mStorage.getPrimaryStorageSize();
+ if (roundedUserspaceBytes <= DataUnit.GIGABYTES.toBytes(512)) {
+ return roundedUserspaceBytes;
+ }
+
+ // Since 1TB devices can actually have either 1000GB or 1024GB,
+ // get the block device size and do just a small rounding if any at all
+ final long totalBytes = mStorage.getInternalStorageBlockDeviceSize();
+ final long totalBytesRounded = FileUtils.roundStorageSize(totalBytes);
+ // If the storage size is 997GB-999GB, round it to a 1000GB to show
+ // 1TB in UI instead of 0.99TB. Same for 2TB, 4TB, 8TB etc.
+ if (totalBytesRounded - totalBytes <= DataUnit.GIGABYTES.toBytes(3)) {
+ return totalBytesRounded;
+ } else {
+ return totalBytes;
+ }
} else {
final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
if (vol == null) {
@@ -286,15 +303,19 @@
// Free space is usable bytes plus any cached data that we're
// willing to automatically clear. To avoid user confusion, this
// logic should be kept in sync with getAllocatableBytes().
+ long freeBytes;
if (isQuotaSupported(volumeUuid, PLATFORM_PACKAGE_NAME)) {
final long cacheTotal = getCacheBytes(volumeUuid, PLATFORM_PACKAGE_NAME);
final long cacheReserved = mStorage.getStorageCacheBytes(path, 0);
final long cacheClearable = Math.max(0, cacheTotal - cacheReserved);
- return path.getUsableSpace() + cacheClearable;
+ freeBytes = path.getUsableSpace() + cacheClearable;
} else {
- return path.getUsableSpace();
+ freeBytes = path.getUsableSpace();
}
+
+ Slog.d(TAG, "getFreeBytes: " + freeBytes);
+ return freeBytes;
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index a9cdf7e..94d4d22 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -791,7 +791,7 @@
AppOpsManager.MODE_ALLOWED);
}
} catch (NameNotFoundException e) {
- // No whitelisted system app on this device
+ // No allowlisted system app on this device
Log.e(LOG_TAG, "Package not found: " + packageName);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4907134..13e5ff1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10408,7 +10408,7 @@
sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
new int[] {4 /* BUSY */});
sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
- sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
+ sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 5000);
sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]);
sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
CellSignalStrengthLte.USE_RSRP);
@@ -10518,7 +10518,7 @@
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"eHRPD", new int[]{10, 400, 600, 800, 1000});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
- "TD_SCDMA", new int[]{1, 100, 500, 1000});
+ "TD_SCDMA", new int[]{1, 50, 100, 500, 1000});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"iDEN", new int[]{1, 2, 10, 50, 100});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
@@ -10536,7 +10536,7 @@
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"EvDo_0", new int[]{300, 600, 1000, 1500, 2000});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
- "1xRTT", new int[]{50, 60, 70, 80});
+ "1xRTT", new int[]{50, 60, 70, 80, 90});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"EDGE", new int[]{154, 169, 183, 192, 267});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 40488b1..145cc2c 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -441,7 +441,7 @@
}
/**
- * Get {@link Context#getOpPackageName()} if this manager has a context, otherwise a dummy
+ * Get {@link Context#getOpPackageName()} if this manager has a context, otherwise a placeholder
* value.
*
* @return The package name to be used for app-ops checks
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5f67441..69b1d63 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7857,7 +7857,7 @@
}
/**
- * Rollback modem configurations to factory default except some config which are in whitelist.
+ * Rollback modem configurations to factory default except some config which are in allowlist.
* Used for device configuration by some carriers.
*
* <p>Requires Permission:
@@ -15351,7 +15351,7 @@
*
* 1) User data is turned on, or
* 2) APN is un-metered for this subscription, or
- * 3) APN type is whitelisted. E.g. MMS is whitelisted if
+ * 3) APN type is allowlisted. E.g. MMS is allowlisted if
* {@link #MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled.
*
* @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index dda021e..67acda0 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -460,7 +460,7 @@
*/
public static final int CODE_LOW_BATTERY = 505;
/**
- * Device declined a call due to a blacklisted caller ID.
+ * Device declined a call due to a denylisted caller ID.
*/
public static final int CODE_BLACKLISTED_CALL_ID = 506;
// IMS -> Telephony
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index c498216..9994002a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -57,7 +57,7 @@
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SATELLITE)
@SystemApi
-public class SatelliteManager {
+public final class SatelliteManager {
private static final String TAG = "SatelliteManager";
private static final ConcurrentHashMap<SatelliteDatagramCallback, ISatelliteDatagramCallback>
@@ -1545,11 +1545,11 @@
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void onDeviceAlignedWithSatellite(boolean isAligned) {
+ public void setDeviceAlignedWithSatellite(boolean isAligned) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- telephony.onDeviceAlignedWithSatellite(mSubId, isAligned);
+ telephony.setDeviceAlignedWithSatellite(mSubId, isAligned);
} else {
throw new IllegalStateException("telephony service is null.");
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0c3991d..06071fe 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2978,7 +2978,7 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void onDeviceAlignedWithSatellite(int subId, in boolean isAligned);
+ void setDeviceAlignedWithSatellite(int subId, in boolean isAligned);
/**
* This API can be used by only CTS to update satellite vendor service package name.
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 1a58f17..fa452dd 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -674,7 +674,7 @@
return true;
}
- // iorap compiler filters specified: the compilerFilter must be in the whitelist.
+ // iorap compiler filters specified: the compilerFilter must be in the allowlist.
if (mIorapCompilerFilters.indexOf(compilerFilter) != -1) {
return true;
}
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt
index ee587f5..03352e3 100644
--- a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt
+++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt
@@ -112,10 +112,10 @@
throw CompanionException("Association result can't be null.")
}
- val association = result.getParcelableExtra(
+ val association = checkNotNull(result.getParcelableExtra(
CompanionDeviceManager.EXTRA_ASSOCIATION,
AssociationInfo::class.java
- )
+ ))
val remoteDevice = association.associatedDevice?.getBluetoothDevice()!!
// Register associated device
@@ -138,8 +138,8 @@
*/
@Rpc(description = "Start permissions sync.")
fun startPermissionsSync(associationId: Int) {
- val pendingIntent = companionDeviceManager
- .buildPermissionTransferUserConsentIntent(associationId)
+ val pendingIntent = checkNotNull(companionDeviceManager
+ .buildPermissionTransferUserConsentIntent(associationId))
CompanionActivity.launchAndWait(context)
CompanionActivity.startIntentSender(pendingIntent)
confirmationUi.waitUntilSystemDataTransferConfirmationVisible()
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
index e56ce81..63782f1 100644
--- a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -45,7 +45,7 @@
private lateinit var mInputMonitor: InputMonitor
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val inputManager = getSystemService(InputManager::class.java)
+ val inputManager = checkNotNull(getSystemService(InputManager::class.java))
mInputMonitor = inputManager.monitorGestureInput(MONITOR_NAME, displayId)
mInputEventReceiver = UnresponsiveReceiver(
mInputMonitor.getInputChannel(), Looper.myLooper()!!)
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
index 807f0c6..e60d8ef 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
@@ -23,6 +23,7 @@
import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.getWindowAndSoftInputFlagParameters;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.hasUnfocusableWindowFlags;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.requestFocusAndVerify;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeAlwaysHiddenWithWindowFlagSet;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
@@ -225,7 +226,7 @@
Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity firstActivity = TestActivity.start(intent1);
// Request view focus after app starts
- mInstrumentation.runOnMainSync(firstActivity::requestFocus);
+ requestFocusAndVerify(firstActivity);
Intent intent2 =
createIntent(
@@ -252,7 +253,7 @@
Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent1);
// Request view focus after app starts
- mInstrumentation.runOnMainSync(activity::requestFocus);
+ requestFocusAndVerify(activity);
// Create second TestActivity
Intent intent2 =
@@ -284,7 +285,7 @@
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
// Request view focus after app starts
- mInstrumentation.runOnMainSync(activity::requestFocus);
+ requestFocusAndVerify(activity);
// Find the editText and click it
UiObject2 editTextUiObject =
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
index 320daee..2ac25f2 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
@@ -23,6 +23,7 @@
import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.requestFocusAndVerify;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
@@ -96,7 +97,7 @@
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
eventually(
() ->
- assertWithMessage("Display rotation should be updated.")
+ assertWithMessage("Display rotation should have been updated")
.that(uiDevice.getDisplayRotation())
.isEqualTo(mIsPortrait ? 0 : 1),
TIMEOUT);
@@ -104,7 +105,7 @@
for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
// TODO(b/291752364): Remove the explicit focus request once the issue with view focus
// change between fullscreen IME and actual editText is fixed.
- callOnMainSync(editText::requestFocus);
+ requestFocusAndVerify(activity);
verifyWindowAndViewFocus(editText, true, true);
callOnMainSync(activity::showImeWithInputMethodManager);
waitOnMainUntilImeIsShown(editText);
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
index 5c02124..5368025 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
@@ -29,6 +29,7 @@
import static com.android.inputmethod.stresstest.ImeStressTestUtil.getWindowAndSoftInputFlagParameters;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.hasUnfocusableWindowFlags;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.isImeShown;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.requestFocusAndVerify;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeAlwaysHiddenWithWindowFlagSet;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
@@ -38,6 +39,9 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
import android.app.Instrumentation;
import android.content.Intent;
import android.os.Build;
@@ -96,7 +100,8 @@
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
// Request focus after app starts to avoid triggering auto-show behavior.
- mInstrumentation.runOnMainSync(activity::requestFocus);
+ requestFocusAndVerify(activity);
+
// Test only once if window flags set to save time.
int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS;
for (int i = 0; i < iterNum; i++) {
@@ -106,21 +111,19 @@
verifyShowBehavior(activity);
callOnMainSync(activity::hideImeWithInputMethodManager);
-
verifyHideBehavior(activity);
}
}
@Test
public void testShowHideWithInputMethodManager_waitingAnimationEnd() {
+ assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
+
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
// Request focus after app starts to avoid triggering auto-show behavior.
- mInstrumentation.runOnMainSync(activity::requestFocus);
+ requestFocusAndVerify(activity);
- if (hasUnfocusableWindowFlags(activity)) {
- return; // Skip to save time.
- }
activity.enableAnimationMonitoring();
EditText editText = activity.getEditText();
for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
@@ -128,12 +131,12 @@
Log.i(TAG, msgPrefix + "start");
callOnMainSync(activity::showImeWithInputMethodManager);
waitOnMainUntil(
- msgPrefix + "IME should be visible",
+ msgPrefix + "IME should have been shown",
() -> !activity.isAnimating() && isImeShown(editText));
callOnMainSync(activity::hideImeWithInputMethodManager);
waitOnMainUntil(
- msgPrefix + "IME should be hidden",
+ msgPrefix + "IME should have been hidden",
() -> !activity.isAnimating() && !isImeShown(editText));
}
}
@@ -141,13 +144,13 @@
@Test
public void testShowHideWithInputMethodManager_intervalAfterHide() {
// Regression test for b/221483132
+ assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
+
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
// Request focus after app starts to avoid triggering auto-show behavior.
- mInstrumentation.runOnMainSync(activity::requestFocus);
- if (hasUnfocusableWindowFlags(activity)) {
- return; // Skip to save time.
- }
+ requestFocusAndVerify(activity);
+
// Intervals = 10, 20, 30, ..., 100, 150, 200, ...
List<Integer> intervals = new ArrayList<>();
for (int i = 10; i < 100; i += 10) intervals.add(i);
@@ -165,14 +168,12 @@
@Test
public void testShowHideWithInputMethodManager_inSameFrame() {
+ assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
// Request focus after app starts to avoid triggering auto-show behavior.
- mInstrumentation.runOnMainSync(activity::requestFocus);
+ requestFocusAndVerify(activity);
- if (hasUnfocusableWindowFlags(activity)) {
- return; // Skip to save time.
- }
// hidden -> show -> hide
mInstrumentation.runOnMainSync(
() -> {
@@ -256,13 +257,12 @@
@Test
public void testShowHideWithWindowInsetsController_waitingVisibilityChange() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
- return;
- }
+ assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
// Request focus after app starts to avoid triggering auto-show behavior.
- mInstrumentation.runOnMainSync(activity::requestFocus);
+ requestFocusAndVerify(activity);
+
// Test only once if window flags set to save time.
int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS;
for (int i = 0; i < iterNum; i++) {
@@ -277,17 +277,13 @@
@Test
public void testShowHideWithWindowInsetsController_waitingAnimationEnd() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
- return;
- }
+ assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+ assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
// Request focus after app starts to avoid triggering auto-show behavior.
- mInstrumentation.runOnMainSync(activity::requestFocus);
+ requestFocusAndVerify(activity);
- if (hasUnfocusableWindowFlags(activity)) {
- return; // Skip to save time.
- }
activity.enableAnimationMonitoring();
EditText editText = activity.getEditText();
for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
@@ -295,29 +291,25 @@
Log.i(TAG, msgPrefix + "start");
mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
waitOnMainUntil(
- msgPrefix + "IME should be visible",
+ msgPrefix + "IME should have been shown",
() -> !activity.isAnimating() && isImeShown(editText));
mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController);
waitOnMainUntil(
- msgPrefix + "IME should be hidden",
+ msgPrefix + "IME should have been hidden",
() -> !activity.isAnimating() && !isImeShown(editText));
}
}
@Test
public void testShowHideWithWindowInsetsController_intervalAfterHide() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
- return;
- }
+ assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+ assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
// Request focus after app starts to avoid triggering auto-show behavior.
- mInstrumentation.runOnMainSync(activity::requestFocus);
+ requestFocusAndVerify(activity);
- if (hasUnfocusableWindowFlags(activity)) {
- return; // Skip to save time.
- }
// Intervals = 10, 20, 30, ..., 100, 150, 200, ...
List<Integer> intervals = new ArrayList<>();
for (int i = 10; i < 100; i += 10) intervals.add(i);
@@ -335,17 +327,13 @@
@Test
public void testShowHideWithWindowInsetsController_inSameFrame() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
- return;
- }
+ assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+ assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
// Request focus after app starts to avoid triggering auto-show behavior.
- mInstrumentation.runOnMainSync(activity::requestFocus);
+ requestFocusAndVerify(activity);
- if (hasUnfocusableWindowFlags(activity)) {
- return; // Skip to save time.
- }
// hidden -> show -> hide
mInstrumentation.runOnMainSync(
() -> {
@@ -377,9 +365,7 @@
@Test
public void testShowWithWindowInsetsController_onCreate_requestFocus() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
- return;
- }
+ assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
// Show with InputMethodManager at onCreate()
Intent intent =
createIntent(
@@ -394,10 +380,8 @@
@Test
public void testShowWithWindowInsetsController_onCreate_notRequestFocus() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
- return;
- }
- // Show and hide with InputMethodManager at onCreate()
+ assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+ // Show and hide with WindowInsetsController at onCreate()
Intent intent =
createIntent(
mWindowFocusFlags,
@@ -411,10 +395,8 @@
@Test
public void testShowWithWindowInsetsController_afterStart_notRequestFocus() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
- return;
- }
- // Show and hide with InputMethodManager at onCreate()
+ assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+ // Show and hide with WindowInsetsController at onCreate()
Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
TestActivity activity = TestActivity.start(intent);
mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
@@ -425,7 +407,8 @@
/**
* Test IME hidden by calling show and hide IME consecutively with
- * {@link android.view.WindowInsetsController} APIs in {@link android.app.Activity#onCreate}.
+ * {@link android.view.WindowInsetsController} APIs in
+ * {@link android.app.Activity#onCreate}.
*
* <p> Note for developers: Use {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_UNCHANGED}
* window flag to avoid some softInputMode visibility flags may take presence over
@@ -436,13 +419,11 @@
*/
@Test
public void testHideWithWindowInsetsController_onCreate_requestFocus() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
- return;
- }
+ assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
if (mSoftInputFlags != SOFT_INPUT_STATE_UNCHANGED) {
return;
}
- // Show and hide with InputMethodManager at onCreate()
+ // Show and hide with WindowInsetsController at onCreate()
Intent intent =
createIntent(
mWindowFocusFlags,
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index 12556bc..c0c60ef 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -40,6 +40,7 @@
import android.widget.EditText;
import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -149,6 +150,14 @@
}
/**
+ * Requests EditText view focus on the main thread, and assert this returns {@code true}.
+ */
+ public static void requestFocusAndVerify(TestActivity activity) {
+ boolean result = callOnMainSync(activity::requestFocus);
+ assertWithMessage("View focus request should have succeeded").that(result).isTrue();
+ }
+
+ /**
* Waits until {@code pred} returns true, or throws on timeout.
*
* <p>The given {@code pred} will be called on the main thread.
@@ -161,7 +170,7 @@
public static void waitOnMainUntilImeIsShown(View view) {
eventually(
() ->
- assertWithMessage("IME should be shown")
+ assertWithMessage("IME should have been shown")
.that(callOnMainSync(() -> isImeShown(view)))
.isTrue(),
TIMEOUT);
@@ -171,27 +180,28 @@
public static void waitOnMainUntilImeIsHidden(View view) {
eventually(
() ->
- assertWithMessage("IME should be hidden")
+ assertWithMessage("IME should have been hidden")
.that(callOnMainSync(() -> isImeShown(view)))
.isFalse(),
TIMEOUT);
}
- /** Waits until window get focus, or throws on timeout. */
+ /** Waits until window gains focus, or throws on timeout. */
public static void waitOnMainUntilWindowGainsFocus(View view) {
eventually(
() ->
- assertWithMessage("Window should gain focus")
+ assertWithMessage(
+ "Window should have gained focus; value of hasWindowFocus:")
.that(callOnMainSync(view::hasWindowFocus))
.isTrue(),
TIMEOUT);
}
- /** Waits until view get focus, or throws on timeout. */
+ /** Waits until view gains focus, or throws on timeout. */
public static void waitOnMainUntilViewGainsFocus(View view) {
eventually(
() ->
- assertWithMessage("View should gain focus")
+ assertWithMessage("View should have gained focus; value of hasFocus:")
.that(callOnMainSync(view::hasFocus))
.isTrue(),
TIMEOUT);
@@ -201,7 +211,7 @@
public static void verifyImeIsAlwaysHidden(View view) {
always(
() ->
- assertWithMessage("IME should be hidden")
+ assertWithMessage("IME should have been hidden")
.that(callOnMainSync(() -> isImeShown(view)))
.isFalse(),
TIMEOUT);
@@ -211,7 +221,8 @@
public static void verifyWindowNeverGainsFocus(View view) {
always(
() ->
- assertWithMessage("window should never gain focus")
+ assertWithMessage(
+ "Window should not have gained focus; value of hasWindowFocus:")
.that(callOnMainSync(view::hasWindowFocus))
.isFalse(),
TIMEOUT);
@@ -221,7 +232,7 @@
public static void verifyViewNeverGainsFocus(View view) {
always(
() ->
- assertWithMessage("view should never gain ime focus")
+ assertWithMessage("View should not have gained focus; value of hasFocus:")
.that(callOnMainSync(view::hasFocus))
.isFalse(),
TIMEOUT);
@@ -254,8 +265,23 @@
}
}
+ /**
+ * Returns {@code true} if the activity can't receive IME focus, based on its window flags,
+ * and {@code false} otherwise.
+ *
+ * @param activity the activity to check.
+ */
public static boolean hasUnfocusableWindowFlags(Activity activity) {
- int windowFlags = activity.getWindow().getAttributes().flags;
+ return hasUnfocusableWindowFlags(activity.getWindow().getAttributes().flags);
+ }
+
+ /**
+ * Returns {@code true} if the activity can't receive IME focus, based on its window flags,
+ * and {@code false} otherwise.
+ *
+ * @param windowFlags the window flags to check.
+ */
+ public static boolean hasUnfocusableWindowFlags(int windowFlags) {
return (windowFlags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0
|| (windowFlags & LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
|| (windowFlags & LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
@@ -302,22 +328,26 @@
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ @NonNull
@Override
public WindowInsetsAnimation.Bounds onStart(
- WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) {
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull WindowInsetsAnimation.Bounds bounds) {
mIsAnimating = true;
return super.onStart(animation, bounds);
}
@Override
- public void onEnd(WindowInsetsAnimation animation) {
+ public void onEnd(@NonNull WindowInsetsAnimation animation) {
super.onEnd(animation);
mIsAnimating = false;
}
+ @NonNull
@Override
public WindowInsets onProgress(
- WindowInsets insets, List<WindowInsetsAnimation> runningAnimations) {
+ @NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
return insets;
}
};
@@ -394,7 +424,7 @@
getInputMethodManager()
.showSoftInput(mEditText, 0 /* flags */);
if (showResult) {
- Log.i(TAG, "IMM#showSoftInput successfully");
+ Log.i(TAG, "IMM#showSoftInput succeeded");
} else {
Log.i(TAG, "IMM#showSoftInput failed");
}
@@ -407,7 +437,7 @@
getInputMethodManager()
.hideSoftInputFromWindow(mEditText.getWindowToken(), 0 /* flags */);
if (hideResult) {
- Log.i(TAG, "IMM#hideSoftInput successfully");
+ Log.i(TAG, "IMM#hideSoftInput succeeded");
} else {
Log.i(TAG, "IMM#hideSoftInput failed");
}
@@ -421,7 +451,7 @@
}
Log.i(TAG, "showImeWithWIC()");
WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController();
- assertWithMessage("WindowInsetsController shouldn't be null.")
+ assertWithMessage("WindowInsetsController")
.that(windowInsetsController)
.isNotNull();
windowInsetsController.show(WindowInsets.Type.ime());
@@ -434,7 +464,7 @@
}
Log.i(TAG, "hideImeWithWIC()");
WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController();
- assertWithMessage("WindowInsetsController shouldn't be null.")
+ assertWithMessage("WindowInsetsController")
.that(windowInsetsController)
.isNotNull();
windowInsetsController.hide(WindowInsets.Type.ime());
@@ -482,13 +512,14 @@
return mIsAnimating;
}
- public void requestFocus() {
- boolean requestFocusResult = getEditText().requestFocus();
+ public boolean requestFocus() {
+ boolean requestFocusResult = mEditText.requestFocus();
if (requestFocusResult) {
- Log.i(TAG, "Request focus successfully");
+ Log.i(TAG, "View#requestFocus succeeded");
} else {
- Log.i(TAG, "Request focus failed");
+ Log.i(TAG, "View#requestFocus failed");
}
+ return requestFocusResult;
}
}
}
diff --git a/tests/Internal/src/stub/DummyWallpaperService.java b/tests/Internal/src/stub/DummyWallpaperService.java
index 084c036..db1b780 100644
--- a/tests/Internal/src/stub/DummyWallpaperService.java
+++ b/tests/Internal/src/stub/DummyWallpaperService.java
@@ -19,7 +19,7 @@
import android.service.wallpaper.WallpaperService;
/**
- * Dummy wallpaper service only for test purposes, won't draw anything.
+ * Placeholder wallpaper service only for test purposes, won't draw anything.
*/
public class DummyWallpaperService extends WallpaperService {
@Override
diff --git a/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java b/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java
index 731be8e..a77950f 100644
--- a/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java
+++ b/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java
@@ -24,7 +24,7 @@
import com.android.internal.compat.IPlatformCompat;
/**
- * This is a dummy API to test gating
+ * This is a placeholder API to test gating
*
* @hide
*/
@@ -36,7 +36,7 @@
public static final long CHANGE_SYSTEM_SERVER = 666016;
/**
- * Dummy method
+ * Placeholder method
* @return "A" if change is enabled, "B" otherwise.
*/
public static String dummyFunc() {
@@ -47,7 +47,7 @@
}
/**
- * Dummy combined method
+ * Placeholder combined method
* @return "0" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is disabled,
"1" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is enabled,
"2" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is disabled,
@@ -68,7 +68,7 @@
}
/**
- * Dummy api using system server API.
+ * Placeholder api using system server API.
*/
public static boolean dummySystemServer(Context context) {
IPlatformCompat platformCompat = IPlatformCompat.Stub
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
index c4bc600..43debb110 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
@@ -98,23 +98,23 @@
(view.getParent() as ViewGroup).removeView(view)
parent.addView(view)
- view.findViewById<Button>(R.id.gainmap_metadata_done)!!.setOnClickListener {
+ view.requireViewById<Button>(R.id.gainmap_metadata_done).setOnClickListener {
closeEditor()
}
- view.findViewById<Button>(R.id.gainmap_metadata_reset)!!.setOnClickListener {
+ view.requireViewById<Button>(R.id.gainmap_metadata_reset).setOnClickListener {
resetGainmapMetadata()
}
updateMetadataUi()
- val gainmapMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
- val gainmapMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
- val capacityMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
- val capacityMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
- val gammaSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
- val offsetSdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
- val offsetHdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
+ val gainmapMinSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
+ val gainmapMaxSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
+ val capacityMinSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
+ val capacityMaxSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
+ val gammaSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_gamma)
+ val offsetSdrSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
+ val offsetHdrSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek,
offsetSdrSeek, offsetHdrSeek).forEach {
it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
@@ -139,13 +139,13 @@
}
private fun updateMetadataUi() {
- val gainmapMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
- val gainmapMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
- val capacityMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
- val capacityMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
- val gammaSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
- val offsetSdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
- val offsetHdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
+ val gainmapMinSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
+ val gainmapMaxSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
+ val capacityMinSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
+ val capacityMaxSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
+ val gammaSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_gamma)
+ val offsetSdrSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
+ val offsetHdrSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
gainmapMinSeek.setProgress(
((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
@@ -165,19 +165,19 @@
((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
.toFloat() * maxProgress).toInt())
- parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val).setText(
"%.3f".format(currentMetadata.ratioMin))
- parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val).setText(
"%.3f".format(currentMetadata.ratioMax))
- parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymin_val).setText(
"%.3f".format(currentMetadata.capacityMin))
- parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymax_val).setText(
"%.3f".format(currentMetadata.capacityMax))
- parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gamma_val).setText(
"%.3f".format(currentMetadata.gamma))
- parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val).setText(
"%.5f".format(currentMetadata.offsetSdr))
- parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_offsethdr_val).setText(
"%.5f".format(currentMetadata.offsetHdr))
}
@@ -200,7 +200,7 @@
private fun updateGainmapMin(normalized: Float) {
val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin)
- parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val).setText(
"%.3f".format(newValue))
currentMetadata.ratioMin = newValue
gainmap.setRatioMin(newValue, newValue, newValue)
@@ -209,7 +209,7 @@
private fun updateGainmapMax(normalized: Float) {
val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax)
- parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val).setText(
"%.3f".format(newValue))
currentMetadata.ratioMax = newValue
gainmap.setRatioMax(newValue, newValue, newValue)
@@ -218,7 +218,7 @@
private fun updateCapacityMin(normalized: Float) {
val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin)
- parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymin_val).setText(
"%.3f".format(newValue))
currentMetadata.capacityMin = newValue
gainmap.setMinDisplayRatioForHdrTransition(newValue)
@@ -227,7 +227,7 @@
private fun updateCapacityMax(normalized: Float) {
val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax)
- parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymax_val).setText(
"%.3f".format(newValue))
currentMetadata.capacityMax = newValue
gainmap.setDisplayRatioForFullHdr(newValue)
@@ -236,7 +236,7 @@
private fun updateGamma(normalized: Float) {
val newValue = minGamma + normalized * (maxGamma - minGamma)
- parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gamma_val).setText(
"%.3f".format(newValue))
currentMetadata.gamma = newValue
gainmap.setGamma(newValue, newValue, newValue)
@@ -248,7 +248,7 @@
if (normalized > 0.0f ) {
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
- parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val).setText(
"%.5f".format(newValue))
currentMetadata.offsetSdr = newValue
gainmap.setEpsilonSdr(newValue, newValue, newValue)
@@ -260,7 +260,7 @@
if (normalized > 0.0f ) {
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
- parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_offsethdr_val).setText(
"%.5f".format(newValue))
currentMetadata.offsetHdr = newValue
gainmap.setEpsilonHdr(newValue, newValue, newValue)
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
index 2f2578b..41baead 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
@@ -190,9 +190,9 @@
sensorManager?.unregisterListener(sensorListener)
}
- override fun onDraw(canvas: Canvas?) {
+ override fun onDraw(canvas: Canvas) {
updateGlassRenderNode()
- canvas?.drawRenderNode(renderNode)
+ canvas.drawRenderNode(renderNode)
}
fun resetGyroOffsets() {
@@ -227,4 +227,4 @@
renderNodeIsDirty = false
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 1400dde..c1a7bd9 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -30,7 +30,7 @@
*/
class LockStateTrackingRule : TestRule {
private val context: Context = getApplicationContext()
- private val windowManager = WindowManagerGlobal.getWindowManagerService()
+ private val windowManager = checkNotNull(WindowManagerGlobal.getWindowManagerService())
@Volatile lateinit var lockState: LockState
private set
diff --git a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
index 4189baa..f1edca3 100644
--- a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
@@ -36,7 +36,7 @@
class ScreenLockRule : TestRule {
private val context: Context = getApplicationContext()
private val uiDevice = UiDevice.getInstance(getInstrumentation())
- private val windowManager = WindowManagerGlobal.getWindowManagerService()
+ private val windowManager = checkNotNull(WindowManagerGlobal.getWindowManagerService())
private val lockPatternUtils = LockPatternUtils(context)
private var instantLockSavedValue = false
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java b/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java
new file mode 100644
index 0000000..7012daf
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test;
+
+import com.android.server.LocalServices;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * JUnit Rule helps override /restore {@link LocalServices} state.
+ */
+public class LocalServiceKeeperRule implements TestRule {
+
+ private final Map<Class<?>, Object> mOverriddenServices = new HashMap<>();
+ private final List<Class<?>> mAddedServices = new ArrayList<>();
+
+ private volatile boolean mRuleApplied = false;
+
+ /**
+ * Overrides service in LocalServices. Service will be restored to original after test run.
+ */
+ public <T> void overrideLocalService(Class<T> type, T service) {
+ if (!mRuleApplied) {
+ throw new IllegalStateException("Can't override service without applying rule");
+ }
+ if (mOverriddenServices.containsKey(type) || mAddedServices.contains(type)) {
+ throw new IllegalArgumentException("Type already overridden: " + type);
+ }
+
+ T currentService = LocalServices.getService(type);
+ if (currentService != null) {
+ mOverriddenServices.put(type, currentService);
+ LocalServices.removeServiceForTest(type);
+ } else {
+ mAddedServices.add(type);
+ }
+ LocalServices.addService(type, service);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ public void evaluate() throws Throwable {
+ try {
+ mRuleApplied = true;
+ base.evaluate();
+ } finally {
+ mRuleApplied = false;
+ mAddedServices.forEach(LocalServices::removeServiceForTest);
+ mOverriddenServices.forEach((clazz, service) -> {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService((Class) clazz, service);
+ });
+ mAddedServices.clear();
+ mOverriddenServices.clear();
+ }
+ }
+ };
+ }
+}
diff --git a/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java b/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java
new file mode 100644
index 0000000..12f2fae
--- /dev/null
+++ b/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+@SmallTest
+public class LocalServiceKeeperRuleTest {
+
+ private final Description mDescription = Description.createSuiteDescription("Description");
+
+ LocalServiceKeeperRule mRule = new LocalServiceKeeperRule();
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(TestService.class);
+ }
+
+ @Test
+ public void testFailedIfCalledOutsideOfTheRule() {
+ TestService service = new TestService() {};
+
+ assertThrows(IllegalStateException.class,
+ () -> mRule.overrideLocalService(TestService.class, service));
+ }
+
+ @Test
+ public void testSetsLocalServiceIfNotPresent() throws Throwable {
+ LocalServices.removeServiceForTest(TestService.class);
+ TestService service = new TestService() {};
+
+ runInRuleApply(() -> {
+ mRule.overrideLocalService(TestService.class, service);
+ assertEquals(service, LocalServices.getService(TestService.class));
+ });
+ }
+
+ @Test
+ public void testOverridesLocalServiceIfPresent() throws Throwable {
+ TestService service = new TestService() {};
+ LocalServices.addService(TestService.class, service);
+ TestService overriddenService = new TestService() {};
+
+ runInRuleApply(() -> {
+ mRule.overrideLocalService(TestService.class, overriddenService);
+ assertEquals(overriddenService, LocalServices.getService(TestService.class));
+ });
+ }
+
+ @Test
+ public void testDoesNotAllowToOverrideSameServiceTwice() throws Throwable {
+ TestService service = new TestService() {};
+
+ runInRuleApply(() -> {
+ mRule.overrideLocalService(TestService.class, service);
+ assertThrows(IllegalArgumentException.class,
+ () -> mRule.overrideLocalService(TestService.class, service));
+ });
+ }
+
+ @Test
+ public void testRestroresLocalServiceAfterTestIfPresent() throws Throwable {
+ TestService expectedService = new TestService() {};
+ LocalServices.addService(TestService.class, expectedService);
+ TestService overriddenService = new TestService() {};
+
+ runInRuleApply(() -> mRule.overrideLocalService(TestService.class, overriddenService));
+
+ assertEquals(expectedService, LocalServices.getService(TestService.class));
+ }
+
+ @Test
+ public void testRemovesLocalServiceAfterTestIfNotPresent() throws Throwable {
+ LocalServices.removeServiceForTest(TestService.class);
+ TestService service = new TestService() {};
+
+ runInRuleApply(() -> mRule.overrideLocalService(TestService.class, service));
+
+ assertNull(LocalServices.getService(TestService.class));
+ }
+
+ private void runInRuleApply(Runnable runnable) throws Throwable {
+ Statement testStatement = new Statement() {
+ @Override
+ public void evaluate() {
+ runnable.run();
+ }
+ };
+ mRule.apply(testStatement, mDescription).evaluate();
+ }
+
+ interface TestService {
+ }
+}